关键的结构体可分成以下几类:
解协议(http、rtsp、rtmp、mms)
AVIOContext
、URLProtocol
、URLContext
主要存储音视频使用的协议类型以及状态。
URLProtocol
存储输入音视频使用的封装格式。每种协议都会有对应的URLProtocol
结构体,文件也不例外。
解封装(flv、avi、rmvb、mp4)
AVFormatContext
主要存储音视频封装格式中的包含的信息。
AVInputFormat
存储输入音视频使用的封装格式,每种音视频封装格式都对应一个AVInputFormat
结构体。
解码(h264、mpeg2、aac、mp3)
每个AVStream
存储一个音频流/视频流的相关数据。
每个AVStream
对应一个AVCodecContext
,存储该流使用的解码方式的相关数据。
每个AVCodecContext
对应一个AVCodec
,包含流对应的解码器器。每种解码器对应一个AVCodec
结构体。
存数据
解码前的数据结构:AVPacket
;解码后的数据结构:AVFrame
。每个结构体有一帧或多帧。
关系:
文件操作
<libavformat/avio.h>
AVIODirContext
操作目录上下文,承载目录信息。
AVIODirEntry
目录内容项,承载文件/目录详细信息。用于存放文件名、文件大小等信息。
1 |
|
数据包操作
<libavformat/avformat.h>
AVFormatContext
统领全局的基本结构体。主要用于处理封装格式。
struct AVInputFormat *iformat
:输入数据的封装格式,由avformat_open_input
设置,仅仅在解封装时使用。struct AVOutputFormat *oformat
:输出数据的封装格式,必须由使用者在avformat_write_header
前设置,由封装时使用。priv_data
:格式私有数据。在封装中,由avformat_write_header
设置;在解封装中,由avformat_open_input
设置。AVIOContext *pb
:输入输出上下文。如果iformat/oformat.flags
设置为AVFMT_NOFILE
的话,该字段不需要设置。对于解封装,需要在avformat_open_input
前设置,或由avformat_open_input
设置;对于封装,在avformat_write_header
前设置。ctx_flags
:码流的信息,表明码流属性的的信号。由libavformat
设置,例如AVFMTCTX_NOHEADER
。nb_streams
:指AVFormatContext.streams
的数量,必须由avformat_new_stream
设置,不能由其他代码改动。AVStream **streams
:文件中所有码流的列表,新的码流创建使用avformat_new_stream
函数。解封装中,码流由avformat_open_input
创建。 如果AVFMTCTX_NOHEADER
被设置,新的码流可以出现在av_read_frame
中。封装中,码流在avformat_write_header
之前由用户创建。它的释放是由avformat_free_context
完成的。filename
:输入或输出的文件名,解封装中由avformat_open_input
设置,封装中在使用avformat_write_header
前由调用者设置。int64_t duration
:码流的时长。bit_rate
:比特率。enum AVCodecID video_codec_id
AVDictionary *metadata
:元数据,适用于整个文件。
1 | // 创建与销毁 |
AVStream
流/轨信息(不包含数据)。
index
:在AVFormatContext的流索引。:已被弃用,改成AVCodecContext *codec
AVCodecParameters *codecpar
:编解码信息。AVRational time_base
:时间单位。duration
:流长度。AVDictionary *metadata
:元数据信息。AVRational avg_frame_rate
:平均帧率。
1 | // 创建与销毁 |
AVIOContext
输入输出对应的结构体。
unsigned char *buffer
:缓存开始位置。buffer_size
:缓存大小。unsigned char *buf_ptr
:当前指针读取到的位置。unsigned char *buf_end
:缓存结束的位置。
1 | // 创建与销毁 |
AVPacket
压缩数据包,压缩域结构体,一个或多个压缩数据帧。这是流操作中的核心数据。
对于视频数据,只包含一帧压缩数据;对于音频数据,可能包含多帧压缩数据。
定义:
1 | typedef struct AVPacket{ |
pts
:显示时间戳,它的单位是AVStream->time_base
;如果在文件中没有保存这个值,它被设置为AV_NOPTS_VALUE
。由于图像显示不可能早于图像解压,因此 PTS 必须比 DTS(解码时间戳)大或者相等。某些文件格式中可能会使用 PTS/DTS 表示其他含义,此时时间戳必须转为真正的时间戳才能保存到 AVPacket 结构中。dts
:解码时间戳,它的单位是AVStream->time_base
,表示压缩视频解码的时间,如果文件中没有保存该值,它被设置为AV_NOPTS_VALUE
。data
:指向真正的压缩编码的数据。size
:表示该 AVPacket 结构中 data 字段所指向的压缩数据的大小。stream_index
:标识该 AVPacket 结构所属的视频流或音频流。duration
:该 AVPacket 包以AVStream->time_base
为单位,所持续的时间,0 表示未知,或者为显示时间戳的差值(next_pts - this pts)。pos
:表示该 AVPacket 数据在媒体中的位置,即字节偏移量。
直接创建:
1 | // 可在栈或堆中创建 |
填充方式一:av_read_frame
通过AVFormatContext可读取一般的媒体容器文件。
这里的读取数据包的方法命名为av_read_frame
只是历史遗留问题,以前的数据包也是用frame命名的,后面才改了过来。
1 | // 每次读取帧后,要对应减引用计数。 |
填充方式二:av_parser_parse2
通过AVCodecParserContext、AVCodecContext解析buffer,得出数据包的主要信息。只能针对音视频裸流进行解析。该方法只是解析,即还要借助其他API读取获得buffer。
AVCodecParser用于解析输入的数据流并把它们分成一帧一帧的压缩编码数据。比较形象的说法就是把长长的一段连续的数据“切割”成一段段的数据。 av_parser_parse2()
:解析数据获得一个Packet, 从输入的数据流中分离出一帧一帧的压缩编码数据。
1 | int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx, uint8_t **poutbuf, int *poutbuf_size, const uint8_t *buf, int buf_size, int64_t pts, int64_t dts, int64_t pos) |
由于传入的buffer可以有多个数据包,所以需要循环读取:
1 | while (data_size > 0) { |
编解码
<libavcodec/avcodec.h>
AVCodec
编码器结构体,通过它转换AVFrame/AVPacket。
定义:
1 | typedef struct AVCodec{ |
name
:具体的 CODEC 的名称的简短描述,比如“HEVC”、“H264”等。long_name:CODEC
名称的详细描述,比如“HEVC (High Efficiency Video Coding)”。id
:唯一标识的 CODEC 类型,比如 AV_CODEC_ID_HEVC。type
:媒体类型的字段,它是 enum 型的,表示视频、音频、字幕等,比如AVMEDIA_TYPE_VIDEO
、AVMEIDA_TYPE_AUDIO
。supported_framerates
:支持的视频帧率的数组,以{0,0}作为结束。pix_fmts
:编解码器支持的图像格式的数组,以 -1 作为结束。profiles
:编解码器支持的 Profile,以 HEVC 为例,包含“Main”、“Main10”、“Main Still Picture”。
每一个编解码器对应一个 AVCodec 结构体,对应一种编解码方式,比如 HEVC、AVC、MPEG2、MPEG4、VP6、VP8、VP9等。以 HEVC 为例,FFMpeg中关于 AVCodec 的定义如下:
1 | AVCodec ff_hevc_decoder = { |
AVCodec通常用法:
- 根据特定ID找到特定的编解码器;
- 根据特定编解码器分配出特定的描述编解码上下文的 AVCodecContext 结构体;
- 打开编解码器;
- 调用编解码器进行编解码。
1 | AVCodec *codec = NULL; |
AVCodecContext
编解码上下文。连接编解码各个过程。最复杂的结构体,里面定义的变量有些是编码时候用到,有些是解码时候用到。
codec_type
:编解码器的类型,如音频、视频、字幕。AVCdec *codec
:编解码器对象。bit_rate
:平均比特率。width
、height
:视频的宽高。refs
:运动估计参考帧的个数。sample_rate
:采样率。channels
:声道数。
AVCodecContext 使用 avcodec_alloc_context3
分配,该函数除了分配 AVCodecContext 外,还会初始化默认的字段。分配的内存必须通过 avcodec_free_context
释放。
1 | // 创建与销毁 |
AVFrame
未压缩数据帧,像素域结构体,(视频对应RGB/YUV像素数据,音频对应PCM采样数据)。
定义:
1 | typedef struct AVFrame{ |
data
:指向图片或信道的指针,与初始化时分配的大小可能不同,一些解码器取数据范围超出(0,0)-(width, height),具体请查看avcodec_align_dimensions2()
方法。一些过滤器或扫描器读数据时可能会超过 16 字节,所以当它们使用时,必须额外分配 16 字节。对于 packed 格式的数据(例如 RGB24),会存放到 data[0] 里面;对于 planar 格式的数据(例如 YUV420P),则会分开 data[0]/data[1]/data[2](YUV420P 中 data[0] 存放 Y,data[1] 存放 U,data[2] 存放 V)。linesize
:对于视频数据,表示每个图像行的字节大小;对于音频数据,表示每个 Plane 的字节大小,只有linesize[0]可以设置,对于plane 音频,每个信道 channel 必须是相同的。对于视频的 linesize 应为 CPU 的对准要求的倍数,一般为 32 的倍数。注意 linesize 可大于可用的数据的尺寸,有可能存在由于性能原因额外填充。width
/height
:视频的宽高。format
:帧格式,-1表示未设置的帧格式。对于视频帧,该值为 enum 类型的 AVPixelFormat,例如AV_PIX_FMT_YUV420P
;对于音频帧,该值为 enum 型的 AVSampleFormat,例如AV_SAMPLE_FMT_S16
。key_frame
:关键帧,1 表示关键帧,0 表示非关键帧。pict_type
:帧图片类型,例如 I/P/B。sample_aspect_ration
:帧像素的宽高比,使用 AVRational 表示。pts
:显示时间戳,单位为time_base
。pkt_pts
:该 PTS 是从 AVPacket 结构中拷贝过来的;与之对应的是pkt_dts
。coded_picture_number
/display_picture_number
:解码序列号和显示序列号(Display Order/Decoded Order)。interlaced_frame
:表示该帧为隔行(interlace)码流或者为逐行(progressive)码流。top_field_first
:对于隔行码流,表示该它是 top first or bottom first。
AVFrame 结构体通常只需分配一次,之后即可通过保存不同的数据来重复多次使用,比如一个 AVFrame 结构可以保存从解码器中解码出的多帧数据。此时,就可以使用av_frame_unref()
释放任何由 Frame 保存的参考帧并还原回最原始的状态。
1 | // 创建与销毁 |