首先介绍一下与解码相关的结构体:

1,AVCodecContext,这个结构体可以是 编码器 的上下文,也可以是 解码器 的上下文,两者使用的是同一种数据结构。

2,AVCodec,编解码信息。

3,AVCodecParameters,编解码参数。

4,AVPacket ,数据包(已编码压缩),这里面的数据通常是一帧视频的数据,或者一帧音频的数据。AVPacket 他本身是没有编码数据的,他只是管理编码数据。

5,AVFrame ,解码之后的 YUV 数据。AVFrame 跟 AVPacket 类似,都是一个管理数据的结构体,他们本身是没有数据的,只是引用了数据。

然后介绍与解码相关的函数有哪些:

1,avcodec_alloc_context3,通过传递 AVCodec 编解码信息来初始化上下文。

2,avcodec_parameters_to_context,把流的 AVCodecParameters 里面的 编解码参数 复制到 AVCodecContext

3,avcodec_open2,打开一个编码器 或者 解码器。

4,avcodec_is_open,判断 一个编码器 或者 解码器 是否打开

4,avcodec_send_packet,往 AVCodecContext 解码器 发送一个 AVPacket

5,avcodec_receive_frame,从 AVCodecContext 解码器 读取一个 AVFrame

AVCodecAVCodecParameters 这两个结构体比较重要。

1,AVCodec 里面放的是 编解码信息

2,AVCodecParameters 里面放的是 编解码参数

怎么理解 编解码信息编解码参数

通常 AVCodec 是使用 avcodec_find_decoder 函数找出来的,你给这个函数一个 AVCodecID,他就能返回一个解码器指针给你。这是 引入 FFmpeg 库的时候,他初始化了一堆静态的编解码变量给你。

例如 AVCodecIDAV_CODEC_ID_H263 ,就会返回 263 相关的 AVCodec 指针, AVCodecIDAV_CODEC_ID_H264 ,就会返回 264 相关的 AVCodec 指针。

只要是用 H264 编码 的视频,使用的解码器信息都是一样的,用的是同一个 AVCodec,但是不同的视频文件,宽高,采样这些信息,肯定会有点不一样。

这些不一样的东西放在哪里呢?就是 AVCodecParameters

avformat_open_input 函数打开一个 MP4 的时候,编码器参数就会放在 codecpar 字段里,如下:

fmt_ctx->streams[0]->codecpar

上面的 codecpar 就是一个 AVCodecParameters,只需要用 avcodec_parameters_to_context 函数把 codecpar 的参数复制给 AVCodecContext 即可。

概括视频解码流程如下:

往一个解码器发一个 AVPacket ,不一定立马就能拿到 一个 AVFrame,因为视频可能有 B 帧,不了解 B 帧的请自行 Google。

所以上面的代码逻辑是,发一个 AVPacket,就 死循环不断的读解码器,直到 返回 EAGAIN,循环是因为有可能有多个 AVFrame 需要读取。

如果返回 EAGAIN ,那就代表解码器需要输入更多的 AVPacket ,才能解码出 AVFrame

如果已经读到 文件末尾,没有 AVPacket 能从容器里面读出来了,怎么办?

这时候就需要 往 解码器发一个 sizedata 都是 0 的 AVPacket ,这样解码器就会把它内部剩余的帧,全部都刷出来。

当解码器完全没有帧可以输出的时候,就会返回 AVERROR_EOF

所以,avcodec_receive_frame() 主要有两个返回值,如下:

1,EAGAIN,代表解码器需要输入更多的 AVPacket ,才能解码出 AVFrame

2,AVERROR_EOF,代表解码器结束了,只有你给他发了 NULLAVPacket 才会返回 AVERROR_EOF,通常只有在读取完文件内容,才会发送 NULLAVPacket 给解码器。

音频解码器也是一样的流程。