在 FFmpeg 學習(六):FFmpeg 核心模塊 libavformat 與 libavcodec 分析 中,咱們分析了FFmpeg中最重要的兩個模塊以及重要的結構體之間的關係。html
後面的文章,咱們先不去繼續瞭解其餘模塊,先針對在以前的學習中接觸到的結構體進行分析,而後在根據功能源碼,繼續瞭解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;
在使用FFMPEG進行開發的時候,AVFormatContext是一個貫穿始終的數據結構,不少函數都要用到它做爲參數。它是FFMPEG解封裝(flv,mp4,rmvb,avi)功能的結構體。下面看幾個主要變量的做用(在這裏考慮解碼的狀況):ide
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兩個屬性。ui
在ffmpeg中經過av_dict_get()函數得到視頻的原數據。編碼
下列代碼顯示了獲取元數據並存入meta字符串變量的過程,注意每一條key和value之間有一個"\t:",value以後有一個"\r\n"url
//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" ; }