FFMPEG 之 parser 一文搞懂FFMPEG 分帧、组帧Framimg

FFMPEG 之 parse_packet

前言

    现实世界中的声音图像采样后经过音视频压缩技术压缩而成的码流称为ES流(Elementary Stream),ES流中包含有解码器解码文件必须的信息,比如视频宽高,采样格式,声音的采样率,声道等等。为了方便传输,播放,将音视频ES数据打包到一个文件中,这个文件称之为音视频ES流的封装,常见的音视频封装格式有:MP3,MP4,AVI,MKV,FLV,RMVB,TS,PS等等。
    解码器源源不断地读取ES流,解码输出,音乐美妙,画面流畅,这一切看起来都是那么的和谐。播放的时候,为了音视频同步,引入了PTS和DTS,即Presentation Time Stamp和Decode Time Stamp 。有的解码器要求将PTS放在ES流的前面几个字节,有的解码器要求将PTS通过描述信息,用描述信息头的方式传入,颇似带内和带外传输。
   这就要求将ES流分成一段一段的,用来将PTS和该段的ES流对应起来,这就是所称的分帧,组帧Framing操作。音频的Framing较为简单,SYNC WORD加上两个SYNC WORD之间的部分就是一帧,SYNC WORD也相对固定。 图像的Framing相对复杂,除上述原因之外,有的解码器要求严格按照图像的边界来描述ES流,如H264的流必须以00 00 00 01 或 00 00 01开始。再者,有些文件在打包的时候,只包含了ES流,无封装格式,比如一些MPEG格式的视频。这些MPEG格式的视频文件后缀可以是mpgv,mpv,mp1v,m1v,mp2v,m2v。所以,这才有了本文需要讲述的packet parse。本文分析基于FFMPEG4.2.2。

数据流向图

    为了兼容各种类型的解码器,播放器在读取原始文件的ES流的时候,在ES流的边界上将文件里面的数据流分成一帧一帧的。这个过程叫做parse。不同类型的ES流,帧的边界不一样,因此parser也不同。在parser设计的时候,切忌将不同类型的parser集中设计,宜采用分治的思路,将不同类型的ES流的parse功能按编码类型设计到独立文件中。本文也认为parser属于adec,vdec的功能,可以将parser放到不同类型的独立性的adec,vdec文件中,这样,包括像frame rate估计,视频宽高等等基本信息的获取,这些工作也可以在parser中完成,实际上FFMPEG中视频基本信息就是在parser中完成的。下面是MPEG VIDEO的帧边界示意图。分析软件为:Elecard Stream Analyzer v.2.3

Parse的过程就是将这些元素切割,分开来。我们把这个切割分开的一段数据叫

parse 流解决的几个问题:

相关的数组结构

与FFMPEG Frmaing相关的重要结构体有两个,AVCodecParserContext和AVCodecParser,说明如下:

AVCodecParserContext

typedef struct AVCodecParserContext {
    /* 格式不同,Framing的方式也不一样,
     * 不同的codec可能需要一些额外的结构来保存Framing的上下文,
     * priv_data就指向这些不同Codec的上下文 */
    void *priv_data;
    /* 特定Codec的parse 方法钩子函数 */
    struct AVCodecParser *parser;
     /* offset of the current frame,
     * AVSTREAM_PARSE_FULL_RAW 时表示当前帧在文件中的位置
     * 其他时候表示输出帧的offset.
     * frame_offset, cur_offset, next_frame_offset三姊妹都仅代表偏移。
     * 个人理解在raw的时候,这三个值之于文件位置才具意义。
     */
    int64_t frame_offset;
    /*
    *  代表了当前包中的ES流在全部demux出来的ES中的偏移。流初始为第一个Pkt的Pos
    *  ----------------------------------------- ---------------------------------------------
    *  cur_offset| pkt2|pkt3|……|pktn|组成的一帧  cur_offset为第一个cur_offset+第一个帧长
    *  ----------------------------------------- ---------------------------------------------
    * 开始时,表示第一个pkt在文件中的位置
    * 找到下一个帧头时,表示下一个帧头相对于第一个PKT的位置,
    * 即cur_offset += frame1_len,cur_offset与文件位置无明确对应关系。
    * 未找到下一个帧头时,表示已经parse过的数据相对于第一个PKT的位置
    * 可以理解成Demux出来的ES数据之间的位置关系。间接描述了文件ES流的长度。
    */
    int64_t cur_offset; 
    /* 始终指向下一个待输出的frame偏移 */
    int64_t next_frame_offset; /* offset of the next frame */
    /* video info, 待输出帧类型,I,P,B,S等等 */
    int pict_type; /* XXX: Put it back in AVCodecContext. */
    /*
     * 用于帧时长计算
     * frame_duration = (1 + repeat_pict) * time_base
     * It is used by codecs like H.264 to display telecined material.
     */
    int repeat_pict; /* XXX: Put it back in AVCodecContext. */
    int64_t pts;     /* pts of the current frame */
    int64_t dts;     /* dts of the current frame */

    /* private data */
    int64_t last_pts;
    int64_t last_dts;
    /* 首次使用或者输出一帧后置位 */
    int fetch_timestamp;

#define AV_PARSER_PTS_NB 4
    /* FFMPEG保存了4个pkt的信息,用于给当前的输出的帧填上合适的POS,pts,dts信息 */
    int cur_frame_start_index;
    /* 偏移信息,4个包相对于ES流起始的偏移 */
    int64_t cur_frame_offset[AV_PARSER_PTS_NB];
    int64_t cur_frame_pts[AV_PARSER_PTS_NB];
    int64_t cur_frame_dts[AV_PARSER_PTS_NB];

    int flags;
#define PARSER_FLAG_COMPLETE_FRAMES           0x0001
#define PARSER_FLAG_ONCE                      0x0002
/// Set if the parser has a valid file offset
#define PARSER_FLAG_FETCHED_OFFSET         &nbs

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值