0%

FFmpeg基本API:重要结构体

关键的结构体可分成以下几类:

解协议(http、rtsp、rtmp、mms)

AVIOContextURLProtocolURLContext主要存储音视频使用的协议类型以及状态。

URLProtocol存储输入音视频使用的封装格式。每种协议都会有对应的URLProtocol结构体,文件也不例外。

解封装(flv、avi、rmvb、mp4)

AVFormatContext主要存储音视频封装格式中的包含的信息。

AVInputFormat存储输入音视频使用的封装格式,每种音视频封装格式都对应一个AVInputFormat结构体。

解码(h264、mpeg2、aac、mp3)

每个AVStream存储一个音频流/视频流的相关数据。

每个AVStream对应一个AVCodecContext,存储该流使用的解码方式的相关数据。

每个AVCodecContext对应一个AVCodec,包含流对应的解码器器。每种解码器对应一个AVCodec结构体。

存数据

解码前的数据结构:AVPacket;解码后的数据结构:AVFrame。每个结构体有一帧或多帧。

关系:

img

文件操作

<libavformat/avio.h>

AVIODirContext

操作目录上下文,承载目录信息。

AVIODirEntry

目录内容项,承载文件/目录详细信息。用于存放文件名、文件大小等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <libavformat/avio.h>

// 删除文件
int avpriv_io_delete(const char* url);

// 移动或重命名
int avpriv_io_move(const char* url_src, const char* url_dst);

// 打开目录,会分配AVIODirContext
int avio_open_dir(AVIODirContext** s, const char* url, AVDictionary** options);

// 读取目录,结果输出到AVIODirEntry
int avio_read_dir(AVIODirContext* s, AVIODirEntry** next);

// 关闭目录(释放资源)
int avio_close_dir(AVIODirContext** s);

数据包操作

<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
2
3
4
5
6
7
8
9
10
11
12
13
// 创建与销毁
AVFormatContext* avformat_alloc_context();
void avformat_free_context(AVFormatContext* s);

/// 其他创建方式

// 打开现有的媒体文件
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
void avformat_close_input(AVFormatContext* s);

// 根据路径猜测并创建。其内部为avformat_alloc_context+av_guess_format+赋值
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename);
void avformat_free_context(AVFormatContext *s)

AVStream

流/轨信息(不包含数据)。

  • index:在AVFormatContext的流索引。
  • AVCodecContext *codec:已被弃用,改成AVCodecParameters *codecpar:编解码信息。
  • AVRational time_base:时间单位。
  • duration:流长度。
  • AVDictionary *metadata:元数据信息。
  • AVRational avg_frame_rate:平均帧率。
1
2
3
// 创建与销毁
AVStream* avformat_new_stream(AVFormatContext* s, const AVCodec* c);
// 随avformat_free_context一起销毁

AVIOContext

输入输出对应的结构体。

  • unsigned char *buffer:缓存开始位置。
  • buffer_size:缓存大小。
  • unsigned char *buf_ptr:当前指针读取到的位置。
  • unsigned char *buf_end:缓存结束的位置。
1
2
3
4
5
6
7
8
9
// 创建与销毁
AVIOContext* avio_alloc_context(
unsigned char* buffer, int buffer_size,
int write_flag, void* opaque,
int(*)(void *opaque, uint8_t *buf, int buf_size) read_packet,
int(*)(void *opaque, uint8_t *buf, int buf_size) write_packet,
int64_t(*)(void *opaque, int64_t offset, int whence) seek
);
void avio_context_free(AVIOContext** s);

AVPacket

压缩数据包,压缩域结构体,一个或多个压缩数据帧。这是流操作中的核心数据。

对于视频数据,只包含一帧压缩数据;对于音频数据,可能包含多帧压缩数据。

定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct AVPacket{
AVBufferRef *buf;
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
AVPacketSideData *side_data;
int side_data_elems;
int duration;
int64_t pos;
int64_t convergence_duration;
}
  • 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
2
3
4
5
// 可在栈或堆中创建

// 类似的,如果AVPacket是在堆创建的,则要配合使用这两个API
AVPacket *av_packet_alloc();
void av_packet_free(AVPacket** pkt);

填充方式一:av_read_frame

通过AVFormatContext可读取一般的媒体容器文件。

这里的读取数据包的方法命名为av_read_frame只是历史遗留问题,以前的数据包也是用frame命名的,后面才改了过来。

1
2
3
// 每次读取帧后,要对应减引用计数。
int av_read_frame(AVFormatContext* s, AVPacket *pkt);
void av_packet_unref(AVPacket* pkt);

填充方式二: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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while (data_size > 0) {
// 返回以解析的大小,若小于buffer的大小,则会再次进行循环解析
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data += ret;
data_size -= ret;

if (pkt->size) {
// 这时获得一个合法的数据包
}
}

编解码

<libavcodec/avcodec.h>

AVCodec

编码器结构体,通过它转换AVFrame/AVPacket。

定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
typedef struct AVCodec{
const char *name;
const char *long_name;
enum AVMediaType type;
enum AVCodecID id;
int capabilities;
const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
uint8_t max_lowres; ///< maximum value for lowres supported by the decoder, no direct access, use av_codec_get_max_lowres()
const AVClass *priv_class; ///< AVClass for the private context
const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
int priv_data_size;
struct AVCodec *next;
int (*init_thread_copy)(AVCodecContext *);
int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);
const AVCodecDefault *defaults;
void (*init_static_data)(struct AVCodec *codec);

int (*init)(AVCodecContext *);
int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,
int *got_packet_ptr);
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
int (*close)(AVCodecContext *);
void (*flush)(AVCodecContext *);
int caps_internal;
}
  • name:具体的 CODEC 的名称的简短描述,比如“HEVC”、“H264”等。
  • long_name:CODEC 名称的详细描述,比如“HEVC (High Efficiency Video Coding)”。
  • id:唯一标识的 CODEC 类型,比如 AV_CODEC_ID_HEVC。
  • type:媒体类型的字段,它是 enum 型的,表示视频、音频、字幕等,比如AVMEDIA_TYPE_VIDEOAVMEIDA_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
AVCodec ff_hevc_decoder = {
.name = "hevc",
.long_name = NULL_IF_CONFIG_SMALL("HEVC (High Efficiency Video Coding)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_HEVC,
.priv_data_size = sizeof(HEVCContext),
.priv_class = &hevc_decoder_class,
.init = hevc_decode_init,
.close = hevc_decode_free,
.decode = hevc_decode_frame,
.flush = hevc_decode_flush,
.update_thread_context = hevc_update_thread_context,
.init_thread_copy = hevc_init_thread_copy,
.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS,
.profiles = NULL_IF_CONFIG_SMALL(profiles),
};

AVCodec通常用法:

  1. 根据特定ID找到特定的编解码器;
  2. 根据特定编解码器分配出特定的描述编解码上下文的 AVCodecContext 结构体;
  3. 打开编解码器;
  4. 调用编解码器进行编解码。
1
2
3
4
5
6
7
AVCodec *codec = NULL;
AVCodecContext *ctx = NULL;

codec = avcodec_find_decoder(origin_ctx->codec_id);
ctx = avcodec_alloc_context3(codec);
avcodec_open2(ctx, codec, NULL);
...

AVCodecContext

编解码上下文。连接编解码各个过程。最复杂的结构体,里面定义的变量有些是编码时候用到,有些是解码时候用到。

  • codec_type:编解码器的类型,如音频、视频、字幕。
  • AVCdec *codec:编解码器对象。
  • bit_rate:平均比特率。
  • widthheight:视频的宽高。
  • refs:运动估计参考帧的个数。
  • sample_rate:采样率。
  • channels:声道数。

AVCodecContext 使用 avcodec_alloc_context3 分配,该函数除了分配 AVCodecContext 外,还会初始化默认的字段。分配的内存必须通过 avcodec_free_context 释放。

1
2
3
// 创建与销毁
AVCodecContext** avcodec_alloc_context3(const AVCodec* codec);
void avcodec_free_context(AVCodecContext** avctx);

AVFrame

未压缩数据帧,像素域结构体,(视频对应RGB/YUV像素数据,音频对应PCM采样数据)。

定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
typedef struct AVFrame{
uint8_t *data[AV_NUM_DATA_POINTERS];
int linesize[AV_NUM_DATA_POINTERS];
uint8_t **extended_data;
int width, height;
int nb_samples; /* number of audio samples(per channel) described by this frame */
int format;
int key_frame; /* 1->keyframe, 0->not*/
enum AVPictureType pict_type;
AVRational sample_aspect_ratio;
int64_t pts;
int64_t pkt_pts;
int64_t pkt_dts;
int coded_picture_number;
int display_picture_number;
int quality;
void *opaque; /* for some private data of the user */
uint64_t error[AV_NUM_DATA_POINTERS];
int repeat_pict;
int interlaced_frame;
int top_field_first; /* If the content is interlaced, is top field displayed first */
int palette_has_changed;
int64_t reordered_opaque;
int sample_rate; /*Sample rate of the audio data*/
uint64_t channel_layout; /*channel layout of the audio data*/
AVBufferRef *buf[AV_NUM_DATA_POINTERS];
AVBufferRef **extended_buf;
int nb_exteneded_buf;
AVFrameSideData **side_data;
int nb_side_data;

int flags;
enum AVColorRange color_range;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
enum AVColorSpace colorspace;
enum AVChromaLocation chroma_location;

int64_t best_effort_timestamp;
int64_t pkt_pos;
int64_t pkt_duration;
AVDictionary *metadata;
int decode _error_flags;

int channels;
int pkt_size;
AVBufferRef *qp_table_buf;
}
  • 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
2
3
4
5
6
7
8
9
// 创建与销毁
AVFrame* av_frame_alloc();
void av_frame_free(AVFrame** frame);

// 填充空间
av_image_fill_arrays(
uint8_t* dst_data[4], int dst_linesize[4], const uint8_t* src,
enum AVPixelFormat pix_fmt, int width, int height, int align
);

欢迎关注我的其它发布渠道