FFmpeg如何使用解码器
首先介绍一下与解码相关的结构体:
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
。
AVCodec
跟 AVCodecParameters
这两个结构体比较重要。
1,AVCodec
里面放的是 编解码信息 。
2,AVCodecParameters
里面放的是 编解码参数。
怎么理解 编解码信息 跟 编解码参数?
通常 AVCodec
是使用 avcodec_find_decoder
函数找出来的,你给这个函数一个 AVCodecID
,他就能返回一个解码器指针给你。这是 引入 FFmpeg 库的时候,他初始化了一堆静态的编解码变量给你。
例如 AVCodecID
是 AV_CODEC_ID_H263
,就会返回 263
相关的 AVCodec
指针, AVCodecID
是 AV_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
能从容器里面读出来了,怎么办?
这时候就需要 往 解码器发一个 size
跟 data
都是 0 的 AVPacket
,这样解码器就会把它内部剩余的帧,全部都刷出来。
当解码器完全没有帧可以输出的时候,就会返回 AVERROR_EOF
。
所以,avcodec_receive_frame()
主要有两个返回值,如下:
1,EAGAIN
,代表解码器需要输入更多的 AVPacket
,才能解码出 AVFrame
。
2,AVERROR_EOF
,代表解码器结束了,只有你给他发了 NULL
的 AVPacket
才会返回 AVERROR_EOF
,通常只有在读取完文件内容,才会发送 NULL
的 AVPacket
给解码器。
音频解码器也是一样的流程。