在 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" ;
}