FFmpeg 結構體學習(一): AVFormatContext 分析

在 FFmpeg 學習(六):FFmpeg 核心模塊 libavformat 與 libavcodec 分析 中,咱們分析了FFmpeg中最重要的兩個模塊以及重要的結構體之間的關係。html

後面的文章,咱們先不去繼續瞭解其餘模塊,先針對在以前的學習中接觸到的結構體進行分析,而後在根據功能源碼,繼續瞭解FFmpeg。數組

AVFormatContext是包含碼流參數較多的結構體。本文將會詳細分析一下該結構體裏每一個變量的含義和做用。緩存

1、源碼整理

首先咱們先看一下結構體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

2、AVForamtContext 重點字段

在使用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" ; }
相關文章
相關標籤/搜索