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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM