博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
FFmpeg 结构体学习(一): AVFormatContext 分析
阅读量:6613 次
发布时间:2019-06-24

本文共 11177 字,大约阅读时间需要 37 分钟。

在  中,我们分析了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        0x0001213 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/dts278      */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 context308      */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      * 强制使用的数据解码器id378      */379     enum AVCodecID data_codec_id;380 381 #if FF_API_OLD_OPEN_CALLBACKS382     /**383      * 需要为解码开启更多的IO contexts时调用384      * @deprecated 已弃用,建议使用io_open and io_close.385      */386     attribute_deprecated387     int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);388 #endif389 390     /**391      * ',' separated list of allowed protocols.392      * - encoding: unused393      * - decoding: set by user394      */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之间有一个"\t:",value之后有一个"\r\n"

//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+"\t:"+value+"\r\n" ;}

 

转载地址:http://lvaso.baihongyu.com/

你可能感兴趣的文章
easy ×××
查看>>
iOS开源项目周报0216
查看>>
大批量linux服务器运维经验
查看>>
java_Socket入门demo2
查看>>
Android掌中游斗地主游戏源码完整版
查看>>
LeetCode - 26. 删除排序数组中的重复项
查看>>
我的友情链接
查看>>
Windows 8.1核心版通过注册表启用Guest账号的局域网共享
查看>>
JQUEY学习第一天之可以编辑的表格
查看>>
[iOS Animation]-CALayer 显示动画 对图层树的动画
查看>>
电脑技术员联盟 Ghost XP Sp3 装机版 V5.8下载 .
查看>>
php 操作数组 (合并,拆分,追加,查找,删除等)
查看>>
Linux LVM逻辑卷配置过程详解
查看>>
(四)SpringBoot+SpringCloud —— Eureka注册中心的机制与配置
查看>>
关于IT服务管理的服务台
查看>>
[deviceone开发]-一个固定列,可以上下左右滑动的表格示例
查看>>
JavaScript细节----解密match(RegExp)
查看>>
Setting up Oozie high availability
查看>>
磁盘与文件系统
查看>>
我的友情链接
查看>>