4.FFMPEG-AVFrame

在ffmpeg中,解码前的数据结构体为AVPacket(参考:3.AVPacket使用),而解码后的数据为AVFrame(视频的YUV, RGB, 音频的PCM,数据量更大)

 

1.AVFrame介绍

  • AVFrame必须使用av_frame_alloc()来分配。注意,这只是分配AVFrame本身,缓冲区的数据(解码成功后的数据)必须通过其他途径被管理.
  • 因为AVFrame通常只分配一次,然后多次复用来保存不同类型的数据,复用的时候需要调用av_frame_unref()将其重置到它前面的原始清洁状态.
  • 注意调用avcodec_receive_frame()时会自动引用减1后再获取frame,所以解码过程中无需每次调用
  • 释放的时候必须用av_frame_free()释放。

 2.常用函数使用

AVFrame *av_frame_alloc(void);
// 初始化
void av_frame_unref(AVFrame *frame);
//引用减1.若为0则释放缓冲区数据,注意调用avcodec_receive_frame()时会自动引用减1后再获取frame,所以解码过程中无需每次调用
void av_frame_free(AVFrame **frame);
//释放frame本身
av_frame_ref(AVFrame *dst, const AVFrame *src);
//从src复制到一个初始化好的dst中,并引用+1
av_frame_clone(const AVFrame *src);
//创建并返回一个复制好的AVPacket(在音视频同步处理中用到该函数)


//获取frame相关函数
int avcodec_send_packet(pCodecCtx, pPacket);    
//发送要解码的数据到解码队列中,并引用+1.返回0表示发送成功

int avcodec_receive_frame(pCodecCtx, pFrame); 
//从解码队列中获取一帧AVFrame,并且获取的AVFrame是已经通过pts排列好的数据,返回0表示获取成功

注意事项:

由于DTS和PTS的顺序可能是不一致的.所以每次视频解码完成后,可能还有几帧frame未显示,
我们需要在末尾通过avcodec_send_packet()传入传递空的AVPacket(NULL)来将最后几帧取出来(音频不需要,因为音频声音都是持续的)

 

2.AVFrame结构体

AVFrame结构体中有很多成员,常见的成员如下所示,注释已替换:

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8

uint8_t *data[AV_NUM_DATA_POINTERS];
//存储原始帧数据(视频的YUV, RGB, 音频的PCM),数组的每一个元素是一个指针,指向了AVBufferRef *buf中的data
//对于packet格式,都存在data[0]中,比如yuv,data[0]中就存的是yuvyuvyuv...,pcm则是lrlrlrlr...
//对于planar格式,则是分开存储,比如yuv,data[0]存y,data[1]存u,data[2]存v,pcm则是data[0]存L,data[1]存R


int linesize[AV_NUM_DATA_POINTERS];
//对于视频,linesize存储的是每个图像的一行(宽)数据大小(字节数)。注意有对齐要求(16或32对齐)
//由于有对齐要求,所以如果width!=linesize[0],那么表示需要行对齐.
//比如yuv420p,那么data[0]里的数据可能就是YYYYY000YYYYY000(0表示是不需要的数据),获取示例如下所示:

  //for(int i = 0; i < height; i++) //获取Y原始数据
  //QByteArray datas[0].append(width,frame->data[0] + frame->linesize[0]*i); //只要width个数据



//对于音频,则是每个data[]通道里的字节大小,并且每个通道(一般就两通道:L和R)的字节数相同 uint8_t **extended_data; //extended_data:*extended_data始终等于data[0]里的成员。 //之所以取名为extended_data,是因为data[]最大只能8个通道. //比如planar格式的pcm的通道数超过了8个,那么就只能使用extended_data来获取数据. int width, height; //视频帧的尺寸(以像素为单位) //用户可以通过if (frame->width > 0 && frame->height > 0)来判断是否为视频流 int nb_samples; //音频帧的单通道样本数据数量(不是以字节为单位,以单个音频数据为单位)
//比如frame的linesize[0]=8192,LR双通道,format为float planar格式(4字节),那么nb_samples=8192/2/4=1024
int format; //帧的格式,如果未知或未设置为-1 //对于视频帧,参考AVPixelFormat枚举值,比如:AV_PIX_FMT_YUV420P //对于音频帧,参考AVSampleFormat枚举值,比如:AV_SAMPLE_FMT_U8 int key_frame; //是否为一幅完整的画面,关键帧(I帧)的标识 //1->关键帧,0->非关键帧 enum AVPictureType pict_type; //视频帧类型(I、B、P等),比如:AV_PICTURE_TYPE_I(I帧) //I帧:一幅完整的画面 //B帧:参考前面和后面两帧的数据加上本帧的变化而得出的本帧数据 //P帧:参考前面而得出的本帧数据. //如果I帧不完整,那么整个GOP(Group of Picture)都是花屏的. /** * Sample aspect ratio for the video frame, 0/1 if unknown/unspecified. */ AVRational sample_aspect_ratio; //像素的宽高比,通过av_q2d()来获取值,如果未知/未指定,为0/1。 /** * Presentation timestamp in time_base units (time when frame should be shown to user). */ int64_t pts; //显示时间戳,表示当前为第几帧,如果要换算为时分秒,则需要AVStream的time_base时基来一起换算 //比如: //int timeVal=av_q2d(pFormatCtx->streams[videoindex]->time_base) * pFrame->pts*100; //int hour = timeVal/360000; //int minute = timeVal%360000/6000; //int second = timeVal%6000/100; //int msec = timeVal%100*10; #if FF_API_PKT_PTS /** * PTS copied from the AVPacket that was decoded to produce this frame. * @deprecated use the pts field instead */ int64_t pkt_pts; //使用pts字段代替(pts=pkt_pts) #endif int64_t pkt_dts; //pkt_dts:解码时间戳,等于AVPacket的dts,如果AVPacket只有dts而未设置pts,此值也是此frame的pts //比如mjpeg格式的视频,就只有I帧,不需要对pts进行排序,所以dts和pts值一样 int coded_picture_number; //编码顺序的图像 int display_picture_number; //播放顺序的图像 /** * quality (between 1 (good) and FF_LAMBDA_MAX (bad)) */ int quality; //视频质量,值越小越好 int repeat_pict; //当解码时,这表示图片必须延迟多少.extra_delay = repeat_pict / (2*fps) int interlaced_frame; //图像逐行/隔行模式标识。 int top_field_first; //如果内容是隔行模式扫描,则首先显示顶部字段。 int palette_has_changed; //用来告诉应用程序,调色板已从前一帧更改。 int64_t reordered_opaque; //重新排序的不透明64位(通常是整数或双精度浮点PTS,但可以是任何东西)。 int sample_rate; //音频数据的采样率。 uint64_t channel_layout; //音频数据的通道布局,参考channel_layout.h //比如AV_CH_FRONT_LEFT:表示前左声道 /** * AVBuffer references backing the data for this frame. If all elements of * this array are NULL, then this frame is not reference counted. This array * must be filled contiguously -- if buf[i] is non-NULL then buf[j] must * also be non-NULL for all j < i. * * There may be at most one AVBuffer per data plane, so for video this array * always contains all the references. For planar audio with more than * AV_NUM_DATA_POINTERS channels, there may be more buffers than can fit in * this array. Then the extra AVBufferRef pointers are stored in the * extended_buf array. */ AVBufferRef *buf[AV_NUM_DATA_POINTERS]; //通过引用计数,使该AVBufferRef来间接使用AVBuffer缓冲区,也就是data[]指向的原始数据. //用户不应直接使用data成员,应通过buf成员间接使用data成员 //如果buf[]的所有元素都为NULL,则此帧不会被引用计数。必须连续填充buf[] - 如果buf[i]为非NULL,则对于所有j<i,buf[j]也必须为非NULL AVBufferRef **extended_buf; int nb_extended_buf; //和extended_data类似,因为buf最多存储8通道. //extended_buf和AVFrame.extended_data唯一不同在于:extended_data包含所有指向各plane的指针,而extended_buf只包含buf中装不下的指针。 AVFrameSideData **side_data; int nb_side_data; //边缘数据和数目 /** * @defgroup lavu_frame_flags AV_FRAME_FLAGS * @ingroup lavu_frame * Flags describing additional frame properties. * * @{ */ #define AV_FRAME_FLAG_CORRUPT (1 << 0) //标记需要解码但不应该输出的帧的标志。帧数据可能被损坏,例如由于解码错误 #define AV_FRAME_FLAG_DISCARD (1 << 2) //标记需要解码但不应该输出的帧的标志。 int flags; //编解码失败后,用户可以通过该flag查看是否为AV_FRAME_FLAG_CORRUPT或者AV_FRAME_FLAG_DISCARD enum AVColorRange color_range; //图像的编码格式(MPEG/JPEG),解码时,由库设置,编码时,由用户来设置 enum AVColorPrimaries color_primaries; //图像源初选的色度坐标 enum AVColorTransferCharacteristic color_trc; //图像颜色传输特性 /** * YUV colorspace type. * - encoding: Set by user * - decoding: Set by libavcodec */ enum AVColorSpace colorspace; //图像彩色空间类型,解码时,由库设置,编码时,由用户来设置 //比如等于AVCOL_SPC_RGB时,那么color_trc等于AVCOL_TRC_IEC61966_2_1 enum AVChromaLocation chroma_location; //储存的颜色色度样品的位置 /** * reordered pos from the last AVPacket that has been input into the decoder * - encoding: unused * - decoding: Read by user. */ int64_t pkt_pos; //标记最后一个解码的packet在输入文件中的位置偏移量。 int64_t pkt_duration; //该帧的持续时间,需要通过AVStream的time_base时基来换算 /** * metadata. * - encoding: Set by user. * - decoding: Set by libavcodec. */ AVDictionary *metadata; int decode_error_flags; //解码帧的错误标志 #define FF_DECODE_ERROR_INVALID_BITSTREAM 1 #define FF_DECODE_ERROR_MISSING_REFERENCE 2 #define FF_DECODE_ERROR_CONCEALMENT_ACTIVE 4 #define FF_DECODE_ERROR_DECODE_SLICES 8 int channels; //音频通道数量,仅用于音频 //用户可以通过 if (frame->nb_samples > 0 && (frame->channel_layout || frame->channels > 0))来判断该frame是否为音频 int pkt_size; //压缩帧的相应数据包的大小。 size_t crop_top; size_t crop_bottom; size_t crop_left; size_t crop_right; //用于裁剪视频帧图像用的。四个值分别为从frame的上/下/左/右边界裁切的像素数。 //... }AVFrame;

 


人间有真情,人间有真爱。

如果您喜欢这里,感觉对你有帮助,并且有多余的软妹币的话,不妨投个食吧,赞赏的时候,留下美句和你的博客地址哦~   戳这里看谁投食了


posted @ 2020-07-06 18:58  诺谦  阅读(897)  评论(0编辑  收藏  举报