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