本文主要讲述ffmpeg编码过程中是如何设置I帧,B帧及P帧的,以及如何通过代码判断帧类型。
之前看过很多网上的文章,讲述如何判断I帧,B帧,P帧,然而都是停留在H.264官方文档中的定义,如果不结合ffmpeg,就仿佛纸上谈兵,有点不切实际,而且很多文章将I帧与I Slice混为一谈,将I Slice当做I帧,这其实是错的。本文就结合ffmpeg讲解ffmpeg中是如何编码各种帧类型的,并纠正其他一些文章的说法。
先有如下定义:
I帧:帧内预测
P帧:前向预测
B帧:前后双向预测
IDR图像:第一个I帧(IDR图像一定是I图像,但I图像不一定是IDR图像),具有随机访问的能力,在IDR帧之后的所有帧都不能引用任何IDR帧之前的帧的内容
(1)IDR帧肯定为I帧
(2)I帧包含了SPS, PPS, I条带
(3)P帧包含P条带
(4)B帧包含B条带
FFMPEG中有以下与帧类型相关的参数:
AVCodecContext* pCodecCtx;
pCodecCtx->gop_size = 25;
pCodecCtx->max_b_frames = 3;
gop_size设置一个gop中有多少帧,默认情况下,一个gop的第一帧为I帧,因此也可以理解为gop_size为相邻两个I帧之间的帧数。max_b_frames设置相邻两个非B帧之间最多出现的B帧数量。
gop有两种类型:封闭式与开放式。封闭式中每个gop的第一帧都是IDR图像,开放式中第一个gop的第一帧是IDR,后续的gop的第一帧非IDR图像。
再看以下编码函数:
int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
第2个参数pFrame类型为AVFrame*,其有参数 pFrame->pict_type,用于设置编码的帧类型(这个值由用户设置,默认值为0,编码器不会自动设置该值,即不会自动设置为I帧,B帧的类型),pict_type的类型为:
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 MPEG4
AV_PICTURE_TYPE_SI, ///< Switching Intra
AV_PICTURE_TYPE_SP, ///< Switching Predicted
AV_PICTURE_TYPE_BI, ///< BI type
};
根据编码输出码流 pkt.data[4] 的值来判断帧类型,其中(pkt.data[4] & 0x1F)即为以下表中的nal_uint_type:
也可以看这个:
这里将一个yuv视频文件编码为H264文件,设置参数如下:
pCodecCtx->max_b_frames = 3;
pFrame->pict_type = AV_PICTURE_TYPE_NONE;
pCodecCtx->gop_size = 25;
以上参数,打印输出如下,其中cNalu =pkt.data[4], type = (