<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
在學習ffmpeg代碼的時候,自始至終要記得你所寫的代碼面對的用戶是程序員,你應該提供一種方式讓他們來輕鬆使用你的api,並容易的做出正確的修改。爲了提高自己的水平,看相當數量的優質代碼是必不可少的一步。
在internal.h中可以看到爲了避免後繼的開發者使用printf和puts等輸出,ffmpeg定義了一些宏:
#undef printf
#define printf please_use_av_log
#undef fprintf
#define fprintf please_use_av_log
#undef puts
#define puts please_use_av_log
#undef perror
#define perror please_use_av_log_instead_of_perror
同樣的對於內存分配等函數,ffmpeg也通過宏定義禁止了。
#undef malloc
#define malloc please_use_av_malloc
#undef free
#define free please_use_av_free
#undef realloc
#define realloc please_use_av_realloc
#undef time
#define time time_is_forbidden_due_to_security_issues
#undef rand
#define rand rand_is_forbidden_due_to_state_trashing_use_av_random
#undef srand
#define srand srand_is_forbidden_due_to_state_trashing_use_av_init_random
#undef random
#define random random_is_forbidden_due_to_state_trashing_use_av_random
#undef sprintf
#define sprintf sprintf_is_forbidden_due_to_security_issues_use_snprintf
#undef strcat
#define strcat strcat_is_forbidden_due_to_security_issues_use_av_strlcat
#undef exit
#define exit exit_is_forbidden
當然我們也可以刪掉這些宏使得可以使用諸如fprintf這樣的函數,log的回調函數當中也是使用了這個函數,不過爲了程序有良好的層次,最好還是使用ffmpeg的接口,此外我們看看ffmpeg對於數字的使用:
#ifndef INT16_MIN
#define INT16_MIN (-0x7fff-1)
#endif
#ifndef INT16_MAX
#define INT16_MAX 0x7fff
#endif
#ifndef INT32_MIN
#define INT32_MIN (-0x7fffffff-1)
#endif
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 0xffffffff
#endif
#ifndef INT64_MIN
#define INT64_MIN (-0x7fffffffffffffffLL-1)
#endif
#ifndef INT64_MAX
#define INT64_MAX INT64_C(9223372036854775807)
#endif
#ifndef UINT64_MAX
#define UINT64_MAX UINT64_C(0xFFFFFFFFFFFFFFFF)
#endif
#ifndef INT_BIT
# if INT_MAX != 2147483647
# define INT_BIT 64
# else
# define INT_BIT 32
# endif
#endif
最後一個INT_BIT的宏定義給出了int類型的位數。可以料想ffmpeg爲了在64位和32位機器上正確使用,當中用了不少技巧,一些內部的定義可以在internal.h中看到。好,再回到ffmpeg的log,關於log的等級,就不必細說了,可以在log4j的文檔當中仔細研究,通用的等級都差不多。我們可以看到log的函數爲:
void av_log(void* avcl, int level, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
av_vlog(avcl, level, fmt, vl);
va_end(vl);
}
av_vlog當中是一個回調函數,這裏就有一個問題了,爲什麼這裏用回調?一般回調我們用在幾種情況:
1. 事件通知。函數想把回調函數作爲一個屬性來處理,這樣他不必知道該通知誰,相當於回調函數註冊了一個地址給函數,函數在特定事件時通知。
舉例來說:
A) 當一個文件處理函數在後臺運行,並且不時的更改界面上顯示的進度條,和其他status時,這時要把進度條更新的函數地址,和status更新的函數地址傳給文件處理函數來回調。
B) 同樣的,不同的模塊之間的消息通信也可以採用把自己的消息添加函數作爲回調函數給對方的方式。
2. 靈活性。我們的函數所調用的處理函數會更改。
舉例來說:
A) 我在一個正則表達式處理函數裏處理一個文本文件,這個文本文件的每一行都會出現一個解析的結果,每行的語義是既定的,這時我希望把不同行的處理函數作爲回調傳給正則表達式處理函數,因爲我實在不想把行語義的邏輯放在底層的函數裏。
B) 同樣的,我有一段讀取文件的代碼,我同時又有一段解碼器的代碼,在讀取文件之前,我已經通過頭信息知道了文件的類型。爲了我的代碼可以方便的註冊解碼器,我希望把解碼器函數作爲回調函數給讀取文件的函數。
3. 狀態的切換。程序工作在不同的模式下,通過運行時修改程序的函數來決定這時在什麼工作狀態。這其實也是一種靈活性,不過稍有特別。
舉例來說:
A) ffmpeg中的av_log_set_callback,我們可以看到,在show_help函數中,這個回調函數的指針被改變了,也就是說log進入了另一種工作模式,log可以是文件,也可以是控制檯輸出,可以是httplog。
B) 引申一下,我們開發的服務器程序一旦發佈,不可避免的有現場問題,一般現場調試的時候我們都會通過telnet方式來登陸這個程序查看一些信息,通過設置回調,我們可以改變程序的一些行爲,來達到調試的目的。
Ffmpeg的log就介紹到這裏,以下是一些題外話:
想寫好的代碼,要讀許多優秀的開源代碼纔可以。
寫出ffmpeg這樣的平臺,不是工作經驗多就可以做到的,對標準的每一個細節都要理解深刻。