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之间有一个"\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" ; }

 

posted @ 2018-07-31 16:30  灰色飘零  阅读(3247)  评论(0编辑  收藏  举报