• FFmpeg 结构体学习(一): AVFormatContext 分析


    在 FFmpeg 学习(六):FFmpeg 核心模块 libavformat 与 libavcodec 分析 中,我们分析了FFmpeg中最重要的两个模块以及重要的结构体之间的关系。

    后面的文章,我们先不去继续了解其他模块,先针对在之前的学习中接触到的结构体进行分析,然后在根据功能源码,继续了解FFmpeg。

    AVFormatContext是包含码流参数较多的结构体。本文将会详细分析一下该结构体里每个变量的含义和作用。

    一、源码整理

    首先我们先看一下结构体AVFormatContext的定义的结构体源码(位于libavformat/avformat.h,本人已经将相关注释翻译成中文,方便大家理解):

      1 /**
      2  * I/O格式上下文
      3  * 
      4  * sizeof(AVFormatContext)方法不能在libav*外部调用,使用avformat_alloc_context()来创建一个AVFormatContext.
      5  */
      6 typedef struct AVFormatContext {
      7     /**
      8      * 一个用来记录和指向avoptions的类。由avformat_all_context()设置。
      9      * 如果(de)muxer存在私有option也会输出。
     10      */
     11     const AVClass *av_class;
     12 
     13     /**
     14      * 输入容器的格式结构体
     15      *
     16      * 只在解码中生成,由avformat_open_input()生成
     17      */
     18     struct AVInputFormat *iformat;
     19 
     20     /**
     21      * 输出容器的格式的结构体
     22      *
     23      * 只在编码中生成后,必须在调用avformat_write_header()方法之前被生成好。
     24      */
     25     struct AVOutputFormat *oformat;
     26 
     27     /**
     28      * 私有数据的格式。这是一个AVOptions-enabled的结构体。
     29      * 当且仅当iformat/oformat.priv_class不为空的时候才会用到。
     30      *
     31      * - 编码时: 由avformat_write_header()设置
     32      * - 解码时: 由avformat_open_input()设置
     33      */
     34     void *priv_data;
     35 
     36     /**
     37      * 输入/输出上下文.
     38      *
     39      * - 解码时: 可以由用户自己设置(在avformat_open_intput()之前,而且必须手动关闭),也可以由avformat_open_input()设置.
     40      * - 编码时: 由用户设置(在avformat_write_header之前).调用者必须注意关闭和释放的问题。
     41      *
     42      * 如果在iformat/oformat.flags里面设置了AVFMT_NOFILE的标志,就不要设置设个字段。 因为在这个情况下,编解码器将以其他的方式进行I/O操作,这个字段将为NULL.
     43      */
     44     AVIOContext *pb;
     45 
     46     /***************************** 流信息相关字段 ***********************************/
     47     /**
     48      * 流属性标志.是AVFMTCTX_*的集合
     49      * 由libavformat设置.
     50      */
     51     int ctx_flags;
     52 
     53     /**
     54      * AVFormatContext.streams -- 流的数量
     55      *
     56      * 由avformat_new_stream()设置,而且不能被其他代码更改.
     57      */
     58     unsigned int nb_streams;
     59     /**
     60      * 文件中所有流的列表.新的流主要由avformat_new_stream()创建.
     61      *
     62      * - 解码时: 流是在avformat_open_input()方法里,由libavformat创建的。如果在ctx_flags里面设置了AVFMTCTX_NOHEADER,那么新的流也可能由av_read_frame()创建.
     63      * - 编码时: 流是由用户创建的(在调用avformat_write_header()之前).
     64      *
     65      * 在avformat_free_context()释放.
     66      */
     67     AVStream **streams;
     68 
     69 #if FF_API_FORMAT_FILENAME
     70     /**
     71      * 输入或输出的文件名
     72      *
     73      * - 解码时: 由avformat_open_input()设置
     74      * - 编码时: 应该在调用avformat_write_header之前由调用者设置
     75      *
     76      * @deprecated 本字段目前已经启用,更改为使用url地址
     77      */
     78     attribute_deprecated
     79     char filename[1024];
     80 #endif
     81 
     82     /**
     83      * 输入或输出的URL. 和旧文件名字段不同的是,这个字段没有长度限制.
     84      *
     85      * - 解码时: 有avformat_open_input()设置, 如果在avformat_open_input()设置的参数为NULL,则初始化为空字符串
     86      * - 编码时: 应该在调用avformat_writer_header()之前由调用者设置(或者调用avformat_init_output_()进行设置),如果在avformat_open_output()设置的参数为NULL,则初始化为空字符串。
     87      *
     88      * 调用avformat_free_context()后由libavformat释放.
     89      */
     90     char *url;
     91 
     92     /**
     93      * 第一帧的时间(AV_TIME_BASE:单位为微秒),不要直接设置这个值,这个值是由AVStream推算出来的。
     94      *
     95      * 仅用于解码,由libavformat设置.
     96      */
     97     int64_t start_time;
     98 
     99     /**
    100      * 流的时长(单位AV_TIME_BASE:微秒)
    101      *
    102      * 仅用于解码时,由libavformat设置.
    103      */
    104     int64_t duration;
    105 
    106     /**
    107      * 所有流的比特率,如果不可用的时候为0。不要设置这个字段,这个字段的值是由FFmpeg自动计算出来的。
    108      */
    109     int64_t bit_rate;
    110 
    111     unsigned int packet_size;
    112     int max_delay;
    113 
    114     /**
    115      * 用于修改编(解)码器行为的标志,由AVFMT_FLAG_*集合构成,需要用户在调用avformat_open_input()或avformat_write_header()之前进行设置
    116      */
    117     int flags;
    118 #define AVFMT_FLAG_*       0x**** //*****
    119 
    120     /**
    121      * 在确定输入格式的之前的最大输入数据量.
    122      * 仅用于解码, 在调用avformat_open_input()之前设置。
    123      */
    124     int64_t probesize;
    125 
    126     /**
    127      * 从avformat_find_stream_info()的输入数据里面读取的最大时长(单位AV_TIME_BASE:微秒)
    128      * 仅用于解码, 在avformat_find_stream_info()设置
    129      * 可以设置0让avformat使用启发式机制.
    130      */
    131     int64_t max_analyze_duration;
    132 
    133     const uint8_t *key;
    134     int keylen;
    135 
    136     unsigned int nb_programs;
    137     AVProgram **programs;
    138 
    139     /**
    140      * 强制使用指定codec_id视频解码器
    141      * 仅用于解码时: 由用户自己设置
    142      */
    143     enum AVCodecID video_codec_id;
    144 
    145     /**
    146      * 强制使用指定codec_id音频解码器
    147      * 仅用于解码时: 由用户自己设置.
    148      */
    149     enum AVCodecID audio_codec_id;
    150 
    151     /**
    152      * 强制使用指定codec_id字母解码器
    153      * 仅用于解码时: 由用户自己设置.
    154      */
    155     enum AVCodecID subtitle_codec_id;
    156 
    157     /**
    158      * 每个流的最大内存索引使用量。
    159      * 如果超过了大小,就会丢弃一些,这可能会使得seek操作更慢且不精准。
    160      * 如果提供了全部内存使用索引,这个字段会被忽略掉.
    161      * - 编码时: 未使用
    162      * - 解码时: 由用户设置
    163      */
    164     unsigned int max_index_size;
    165 
    166     /**
    167      * 最大缓冲帧的内存使用量(从实时捕获设备中获得的帧数据)
    168      */
    169     unsigned int max_picture_buffer;
    170 
    171     /**
    172      * AVChapter数组的数量
    173      */
    174     unsigned int nb_chapters;
    175     AVChapter **chapters;
    176 
    177     /**
    178      * 整个文件的元数据
    179      *
    180      * - 解码时: 在avformat_open_input()方法里由libavformat设置
    181      * - 编码时: 可以由用户设置(在avformat_write_header()之前)
    182      *
    183      * 在avformat_free_context()方法里面由libavformat释放
    184      */
    185     AVDictionary *metadata;
    186 
    187     /**
    188      * 流开始的绝对时间(真实世界时间)
    189      */
    190     int64_t start_time_realtime;
    191 
    192     /**
    193      * 用于确定帧速率的帧数
    194      * 仅在解码时使用
    195      */
    196     int fps_probe_size;
    197 
    198     /**
    199      * 错误识别级别.
    200      */
    201     int error_recognition;
    202 
    203     /**
    204      * I/O层的自定义中断回调.
    205      */
    206     AVIOInterruptCB interrupt_callback;
    207 
    208     /**
    209      * 启动调试的标志
    210      */
    211     int debug;
    212 #define FF_FDEBUG_TS        0x0001
    213 
    214     /**
    215      * 最大缓冲持续时间
    216      */
    217     int64_t max_interleave_delta;
    218 
    219     /**
    220      * 允许非标准扩展和实验
    221      */
    222     int strict_std_compliance;
    223 
    224     /**
    225      * 检测文件上发生事件的标志
    226      */
    227     int event_flags;
    228 #define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 
    229 
    230     /**
    231      * 等待第一个事件戳要读取的最大包数
    232      * 仅解码 
    233      */
    234     int max_ts_probe;
    235 
    236     /**
    237      * 在编码期间避免负时间戳.
    238      * 值的大小应该是AVFMT_AVOID_NEG_TS_*其中之一.
    239      * 注意,这个设置只会在av_interleaved_write_frame生效
    240      * - 编码时: 由用户设置
    241      * - 解码时: 未使用
    242      */
    243     int avoid_negative_ts;
    244 #define AVFMT_AVOID_NEG_TS_*
    245 
    246     /**
    247      * 传输流id.
    248      * 这个将被转移到解码器的私有属性. 所以没有API/ABI兼容性
    249      */
    250     int ts_id;
    251 
    252     /**
    253      * 音频预加载时间(单位:毫秒)
    254      * 注意:并非所有的格式都支持这个功能,如果在不支持的时候使用,可能会发生不可预测的事情.
    255      * - 编码时: 由用户设置
    256      * - 解码时: 未使用
    257      */
    258     int audio_preload;
    259 
    260     /**
    261      * 最大块时间(单位:微秒).
    262      * 注意:并非所有格式都支持这个功能,如果在不支持的时候使用,可能会发生不可预测的事情.
    263      * - 编码时: 由用户设置
    264      * - 解码时: 未使用
    265      */
    266     int max_chunk_duration;
    267 
    268     /**
    269      * 最大块大小(单位:bytes)
    270      * 注意:并非所有格式都支持这个功能,如果在不支持的时候使用,可能会发生不可预测的事情.
    271      * - 编码时: 由用户设置
    272      * - 解码时: 未使用
    273      */
    274     int max_chunk_size;
    275 
    276     /**
    277      * 强制使用wallclock时间戳作为数据包的pts/dts
    278      */
    279     int use_wallclock_as_timestamps;
    280 
    281     /**
    282      * avio标志
    283      */
    284     int avio_flags;
    285 
    286     /**
    287      * 可以用各种方法估计事件的字段
    288      */
    289     enum AVDurationEstimationMethod duration_estimation_method;
    290 
    291     /**
    292      * 打开流时跳过初始字节
    293      */
    294     int64_t skip_initial_bytes;
    295 
    296     /**
    297      * 纠正单个时间戳溢出
    298      */
    299     unsigned int correct_ts_overflow;
    300 
    301     /**
    302      * 强制寻找任何帧
    303      */
    304     int seek2any;
    305 
    306     /**
    307      * 在每个包只会刷新I/O context
    308      */
    309     int flush_packets;
    310 
    311     /**
    312      * 格式探索得分
    313      */
    314     int probe_score;
    315 
    316     /**
    317      * 最大读取字节数(用于识别格式)
    318      */
    319     int format_probesize;
    320 
    321     /**
    322      * 允许的编码器列表(通过','分割)
    323      */
    324     char *codec_whitelist;
    325 
    326     /**
    327      * 允许的解码器列表(通过','分割 )
    328      */
    329     char *format_whitelist;
    330 
    331     ......./**
    332      * 强制视频解码器
    333      */
    334     AVCodec *video_codec;
    335 
    336     /**
    337      * 强制音频解码器
    338      */
    339     AVCodec *audio_codec;
    340 
    341     /**
    342      * 强制字母解码器
    343      */
    344     AVCodec *subtitle_codec;
    345 
    346     /**
    347      * 强制数据解码器
    348      */
    349     AVCodec *data_codec;
    350 
    351     /**
    352      * 在元数据头中写入填充的字节数
    353      */
    354     int metadata_header_padding;
    355 
    356     /**
    357      * 用户数据(放置私人数据的地方)
    358      */
    359     void *opaque;
    360 
    361     /**
    362      * 用于设备和应用程序之间的回调
    363      */
    364     av_format_control_message control_message_cb;
    365 
    366     /**
    367      * 输出时间戳偏移量(单位:微秒)
    368      */
    369     int64_t output_ts_offset;
    370 
    371     /**
    372      * 转储格式分隔符
    373      */
    374     uint8_t *dump_separator;
    375 
    376     /**
    377      * 强制使用的数据解码器id
    378      */
    379     enum AVCodecID data_codec_id;
    380 
    381 #if FF_API_OLD_OPEN_CALLBACKS
    382     /**
    383      * 需要为解码开启更多的IO contexts时调用
    384      * @deprecated 已弃用,建议使用io_open and io_close.
    385      */
    386     attribute_deprecated
    387     int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
    388 #endif
    389 
    390     /**
    391      * ',' separated list of allowed protocols.
    392      * - encoding: unused
    393      * - decoding: set by user
    394      */
    395     char *protocol_whitelist;
    396 
    397     /**
    398      * 打开新IO流的回调
    399      */
    400     int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
    401                    int flags, AVDictionary **options);
    402 
    403     /**
    404      * 关闭流的回调(流是由AVFormatContext.io_open()打开的)
    405      */
    406     void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
    407 
    408     /**
    409      * ',' 单独的不允许的协议的列表
    410      * - 编码: 没使用到
    411      * - 解码: 由用户设置
    412      */
    413     char *protocol_blacklist;
    414 
    415     /**
    416      * 最大流数
    417      * - 编码: 没使用到
    418      * - 解码: 由用户设置
    419      */
    420     int max_streams;
    421 } AVFormatContext;
    View Code

    二、AVForamtContext 重点字段

    在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。下面看几个主要变量的作用(在这里考虑解码的情况):

    struct AVInputFormat *iformat:输入数据的封装格式
    AVIOContext *pb:输入数据的缓存
    unsigned int nb_streams:视音频流的个数
    AVStream **streams:视音频流
    char filename[1024]:文件名
    int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
    int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
    AVDictionary *metadata:元数据

    视频的时长可以转换成HH:MM:SS的形式,示例代码如下:

    AVFormatContext *pFormatCtx;
    CString timelong;
    ...
    //duration是以微秒为单位
    //转换成hh:mm:ss形式
    int tns, thh, tmm, tss;
    tns  = (pFormatCtx->duration)/1000000;
    thh  = tns / 3600;
    tmm  = (tns % 3600) / 60;
    tss  = (tns % 60);
    timelong.Format("%02d:%02d:%02d",thh,tmm,tss);

    视频的原数据(metadata)信息可以通过AVDictionary获取。元数据存储在AVDictionaryEntry结构体中,如下所示:

    typedef struct AVDictionaryEntry {
        char *key;
        char *value;
    } AVDictionaryEntry;

    每一条元数据分为key和value两个属性。

    在ffmpeg中通过av_dict_get()函数获得视频的原数据。

    下列代码显示了获取元数据并存入meta字符串变量的过程,注意每一条key和value之间有一个" :",value之后有一个" "

    //MetaData------------------------------------------------------------
    //从AVDictionary获得
    //需要用到AVDictionaryEntry对象
    //CString author,copyright,description;
    CString meta=NULL,key,value;
    AVDictionaryEntry *m = NULL;
    //不用一个一个找出来
    /*    
    m=av_dict_get(pFormatCtx->metadata,"author",m,0); author.Format("作者:%s",m->value); m=av_dict_get(pFormatCtx->metadata,"copyright",m,0); copyright.Format("版权:%s",m->value); m=av_dict_get(pFormatCtx->metadata,"description",m,0); description.Format("描述:%s",m->value);
    */ //使用循环读出 //(需要读取的数据,字段名称,前一条字段(循环时使用),参数) while(m=av_dict_get(pFormatCtx->metadata,"",m,AV_DICT_IGNORE_SUFFIX)){ key.Format(m->key); value.Format(m->value); meta+=key+" :"+value+" " ; }
  • 相关阅读:
    python 字典dict
    前端笔记之HTML
    Activity工作流框架入门(二)API使用DEMO
    工作流Activity框架入门(一)
    使用Ecplise git commit时出现"There are no stages files"
    安装Eclipse activity插件 报异常 Cannot complete the install because one or more required items could not be
    JQuery--使用JQuery 的$.ajax 方法进行异步请求,导致页面闪烁
    JQuery--JQuery面向对象编程快速入门-插件开发
    VIM编辑器常用命令
    Git入门
  • 原文地址:https://www.cnblogs.com/renhui/p/9361276.html
Copyright © 2020-2023  润新知