mux:视频/音频封装(合成、混合、复用)
demux:解封装视频/音频(分离,分割,解复用)
* Libavformat (lavf) is a library for dealing with various media container
* formats. Its main two purposes are demuxing - i.e. splitting a media file
* into component streams, and the reverse process of muxing - writing supplied
* data in a specified container format. It also has an @ref lavf_io
* "I/O module" which supports a number of protocols for accessing the data (e.g.
* file, tcp, http and others).
* Main lavf structure used for both muxing and demuxing is AVFormatContext,
* which exports all information about the file being read or written.
区分AVPacket和AVFrame两个结构体。
AVPacket表示一幅经过了关键帧或过渡帧编码后的画面,
AVFrame表示一个AVPacket经过解码后的完整YUV画面
AVFormatContext:统领全局的基本结构体。主要用于处理封装格式(FLV/MKV/RMVB等)。
AVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。
AVStream,AVCodecContext:视音频流对应的结构体,用于视音频编解码。
AVFrame:存储非压缩的数据(视频对应RGB/YUV像素数据,音频对应PCM采样数据)
AVPacket:存储压缩数据(视频对应H.264等码流数据,音频对应AAC/MP3等码流数据)
FFMPEG中最关键的结构体之间的关系_雷霄骅的博客-优快云博客_ffmpeg 结构体
FFMPEG中结构体很多。最关键的结构体可以分成以下几类:
a) 解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)
b) 解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。
c) 解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。
d) 存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧
解码前数据:AVPacket
解码后数据:AVFrame
他们之间的对应关系如下所示:
看看 void av_opt_set_defaults(void *s),它就是典型了运用了无类型指针 数据来进行传递数据。但是 void av_opt_set_defaults2(void *s, int mask, int flags)中,需要使用 const AVOption *av_next_option(void *obj, const AVOption *last)函数来寻找一个 AVOption, 实际上 av_next_option 才是我们第一个要分析的重点所在。对于一个无类型指针数据,它是如何找到 AVOption,其中的一行很关键:(*(AVClass **)obj)->option; 要知道我们最开始传入的 是一个 AVFormatContext 即当前的(*(AVClass **)obj),这两者如何能够划上等号?只有一种 情况是 AVFormatContext 的第一个成员必须是 AVClass 类型,事实上正是如此,这就是 av_opt_set_defaults 能够得到正确执行的基础。 重点: 任何需要执行 void av_opt_set_defaults(void *s)的数据类型的第一个成 员必须是 AVClass 类型。 找到了一个 AVOption,就有了具体操作的规则,但是要操作的对象仍然是 AVFormatContext,那如何设置到 AVFormatContext 呢?通过上面的代码,可以看到任何一 个设置最终都是 static int av_set_number2(void *obj
FFmpeg源代码简单分析:结构体成员管理系统-AVClass_雷霄骅的博客-优快云博客
AVOption可以使用字符串为任何类型的变量赋值。传统意义上,如果变量类型为int,则需要使用整数来赋值;如果变量为double,则需要使用小数来赋值;如果变量类型为char *,才需要使用字符串来赋值。而AVOption将这些赋值“归一化”了,统一使用字符串赋值。例如给int型变量qp设定值为20,通过AVOption需要传递进去一个内容为“20”的字符串。
此外,AVOption中变量的名称也使用字符串来表示。结合上面提到的使用字符串赋值的特性,我们可以发现使用AVOption之后,传递两个字符串(一个是变量的名称,一个是变量的值)就可以改变系统中变量的值。
上文提到的这种方法的意义在哪里?我个人感觉对于直接使用C语言进行开发的人来说,作用不是很明显:完全可以使用等于号“=”就可以进行各种变量的赋值。但是对于从外部系统中调用FFmpeg的人来说,作用就很大了:从外部系统中只可以传递字符串给内部系统。比如说对于直接调用ffmpeg.exe的人来说,他们是无法修改FFmpeg内部各个变量的数值的,这种情况下只能通过输入“名称”和“值”这样的字符串,通过AVOption改变FFmpeg内部变量的值。由此可见,使用AVOption可以使FFmpeg更加适应多种多样的外部系统。
AVClass最主要的作用就是给结构体(例如AVFormatContext等)增加AVOption功能的支持。换句话说AVClass就是AVOption和目标结构体之间的“桥梁”。AVClass要求必须声明为目标结构体的第一个变量。
AVClass中有一个option数组用于存储目标结构体的所有的AVOption。举个例子,AVFormatContext结构体,AVClass和AVOption之间的关系如下图所示。
图中AVFormatContext结构体的第一个变量为AVClass类型的指针av_class,它在AVFormatContext结构体初始化的时候,被赋值指向了全局静态变量av_format_context_class结构体(定义位于libavformat\options.c)。而AVClass类型的av_format_context_class结构体中的option变量指向了全局静态数组avformat_options(定义位于libavformat\options_table.h)。
AVOption的几个成员变量:
name:名称。
help:简短的帮助。
offset:选项相对结构体首部地址的偏移量(这个很重要)。
type:选项的类型。
default_val:选项的默认值。
min:选项的最小值。
max:选项的最大值。
flags:一些标记。
unit:该选项所属的逻辑单元,可以为空。
其中,default_val是一个union类型的变量,可以根据选项数据类型的不同,取int,double,char*,AVRational(表示分数)几种类型。type是一个AVOptionType类型的变量。AVOptionType是一个枚举类型
AVClass的几个已经理解的成员变量:
class_name:AVClass名称。
item_name:函数,获取与AVClass相关联的结构体实例的名称。
option:AVOption类型的数组(最重要)。
version:完成该AVClass的时候的LIBAVUTIL_VERSION。
category:AVClass的类型,是一个类型为AVClassCategory的枚举型变量。
enum AVPictureType {
AV_PICTURE_TYPE_NONE = 0, ///< Undefined
AV_PICTURE_TYPE_I, ///< Intra
AV_PICTURE_TYPE_P, ///< Predicted
AV_PICTURE_TYPE_B, ///< Bi-dir predicted
AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG-4
AV_PICTURE_TYPE_SI, ///< Switching Intra
AV_PICTURE_TYPE_SP, ///< Switching Predicted
AV_PICTURE_TYPE_BI, ///< BI type
};
关于音视频的一些知识(demux、filter等)_HAOMCU的博客-优快云博客
MUX和DEMUX
Mux 是 Multiplex 的缩写,意为“多路传输”,其实就是“混流”、“封装”的意思,与“合成”的意思相似就是指把视频素材和音频素材封装到一个单独的文件中。
muxing 是在mux 后面加了 -ing 构成的动名词形式。
Demux是在 mux 前面加了个表示否定的 De- 前缀,意思是进行与 muxing 相反的“分解复用”操作,也就是我们平时说的“分离”一个文件中的视频部分或是音频部分。
同样,也可以在 demux 后面加 -ing 构成动名词 demuxing。
意义:
通过 muxing(混流),可以将视频流、音频流甚至是字幕流捆绑到一个单独的文件中,作为一个信号进行传输,等传输完毕,就可以通过 demuxing(分离) 将里面的视频、音频或字幕分解出来各自进行解码和播放。
要点:
在 muxing 与 demuxing 的整个过程,都不对原来的视频、音频或字幕重新编码。混流(封装、打包)后的文件,可以通过分离(分解、解包)操作,获得与原始素材一模一样的独立的视频、音频和字幕文件。
视频的分离与合成,编码和解码
对媒体流的处理分为两种:“编码(encoding)”和“解码(decoding)”。编码指的是通过一定协议或规则把一段声音或图像转化成计算机数字文件的过程。而解码恰恰是编码的反面——把编码过的媒体文件重新转化成声音或图像。
用来执行编码工作的软件叫“编码器”(Coder 或 Encoder);
用来执行解码工作的软件叫“解码器”(Decoder)
“编码器”与“解码器”合称“编解码器”(“Codec”)。
声音与图像是两种不同的媒体,它们分别对应人的两种不同感官。作为不同的媒体,我们只能用专门针对声音的软件或是专门针对视频的软件去分别分析处理音频流(Audio Stream)与视频流(Video Stream)。
用来编码音频流的软件叫作“音频编码器”(Audio Encoder)
用来编码视频流的软件叫作“视频编码器”(Video Encoder)
用来解码音频流的软件叫作“音频解码器”(Audio Decoder)
用来解码视频流的软件叫作“视频解码器”(Video Decoder)
音频流与视频流的处理必须分别进行,即:
“音频编码器”编码出单个音频文件,
“视频编码器”编码出单个视频文件,
“音频解码器”单独对音频文件进行解码还原,
“视频解码器”单独对视频文件进行解码还原。
既然音频处理和视频处理必须单独运行,那为什么我们平时看的RMVB、AVI格式的电影都是既包含声音又包含图像的单个文件呢?那是因为我们在通过摄像机获得单独的音频流和视频流后不仅对它们进行了“编码”,还对它们进行了“合成”(Synthesis)。通过合成,音频与视频就打包到一起,生成一个单独的文件。可以说,所有既有声音又有图像的视频文件,100%都是通过某种合成器(Synthesizer)合成过的。(注意:“合成”与“合并”不同,见最后的参考帖子。)
然而必须知道的是:尽管通过合成器可以把音频流和视频流打包成一个文件,但是正如人的眼睛不可能听、人的耳朵不可能看,音频流和视频流是不可能完全地混杂到一起的,是注定“分离”的。因此,所谓“合成”,只是把音频流和视频流用一个容器文件(Container)封装起来,其实里面还是各自独立的。我们在播放视频文件的时候总是先调用分离器(Splitter),将封装合成的视频“分离”成独立的音频和视频码流,然后才调用解码器对这些独立的音频流和视频流进行解码输出。
举个例子应该比较好懂一些:
比如我们有一个音频文件 Sample.mp2 和一个视频文件 Sample.m1v,用编码软件如小日本4通过 MPEG-1 编码方式合成为一个独立文件 Sample.mpg,然后把这个.mpg文件拿到媒体播放器里面播放,直接就可以听到声音看到画面。表面上看播放器只是简单地“播放”了这个文件,实际上这个“播放”包含了更多我们看不到的步骤。这个步骤是这样的:
播放器打开视频源文件
播放器调用分离器将视频文件分解为单独的音频流和视频流
播放器调用音频解码器对音频流进行解码,同时调用视频解码器对视频流进行解码
播放器依据同样的时间线将解码后的音频流和视频流输出到播放窗口并使之保持同步。
我们经常听到滤镜(Filter)的名称,实际上就是指的各种分离器或解码器。
容器/文件(Conainer/File):即特定格式的多媒体文件,比如mp4、flv、mkv等。
媒体流(Stream):表示时间轴上的一段连续数据,如一段声音数据、一段视频数据或一段字幕数据,可以是压缩的,也可以是非压缩的,压缩的数据需要关联特定的编解码器。
数据帧/数据包(Frame/Packet):通常,一个媒体流是由大量的数据帧组成的,对于压缩数据,帧对应着编解码器的最小处理单元,分属于不同媒体流的数据帧交错存储于容器之中。
一般情况下:
Frame对应压缩前的数据,Packet对应压缩后的数据。
编解码器(Codec):以帧为单位实现压缩数据和原始数据之间的相互转换的
复用(mux):把不同的流按照某种容器的规则放入容器,这种行为叫做复用(mux)
解复用(mux):把不同的流从某种容器中解析出来,这种行为叫做解复用(demux)