日誌系統
PS:
在當前的計算機系統中, I/O操作相比其它環節是最耗費資源浪費時間的,所以大家要慎重, 儘量減少不必要的I/O操作。
使用一般步驟:
- 頭文件: #include <libavutil/log.h>
- 設置log級別: av_log_set_level(AV_LOG_DEBUG);
- 輸出log: av_log(nullptr, AV_LOG_INFO, “this is log output”);
設置日誌級別
設置日誌級別的意義:作者可以預設標識了級別的輸出日誌, 然後通過設置日誌級別來控制那些日誌信息需要輸出, 方便進行調試。而不需要到處去修改,和設置DEBUG宏異曲同工之妙。
/**
* Set the log level
*
* @see lavu_log_constants
*
* @param level Logging level
*/
void av_log_set_level(int level);
函數代碼實現:
static int av_log_level = AV_LOG_INFO;
顯然,設置日誌輸出等級就是操作靜態全局變量av_log_level。該變量用於存儲當前系統Log的級別。
設置了日誌輸出級別之後, 比此級別嚴重的日誌都會輸出
exp:
int main()
{
av_log_set_level(AV_LOG_INFO);
av_log(nullptr, AV_LOG_INFO, "AV_LOG_INFO = %d\n", AV_LOG_INFO);
av_log(nullptr, AV_LOG_ERROR, "AV_LOG_ERROR = %d\n", AV_LOG_ERROR);
av_log(nullptr, AV_LOG_TRACE, "AV_LOG_TRACE = %d\n", AV_LOG_TRACE);
return 0;
}
int main()
{
av_log_set_level(AV_LOG_ERROR);
av_log(nullptr, AV_LOG_INFO, "AV_LOG_INFO = %d\n", AV_LOG_INFO);
av_log(nullptr, AV_LOG_ERROR, "AV_LOG_ERROR = %d\n", AV_LOG_ERROR);
av_log(nullptr, AV_LOG_TRACE, "AV_LOG_TRACE = %d\n", AV_LOG_TRACE);
return 0;
}
常用的日誌級別
// Print no output.
#define AV_LOG_QUIET -8
// Something went really wrong and we will crash now.
#define AV_LOG_PANIC 0
// Something went wrong and recovery is not possible.For example, no header was found for a format
//which depends on headers or an illegal combination of parameters is used.
#define AV_LOG_FATAL 8
// Something went wrong and cannot losslessly be recovered.However, not all future data is affected.
#define AV_LOG_ERROR 16
// Something somehow does not look correct. This may or may not lead to problems.
// An example would be the use of '-vstrict -2'.
#define AV_LOG_WARNING 24
// Standard information.
#define AV_LOG_INFO 32
// Detailed information.
#define AV_LOG_VERBOSE 40
// Stuff which is only useful for libav* developers.
#define AV_LOG_DEBUG 48
// Extremely verbose debugging, useful for libav* development.
#define AV_LOG_TRACE 56
每個級別定義的數值代表了嚴重程度,數值越小代表越嚴重。
默認av_log()輸出的級別是AV_LOG_INFO。
獲取當前日誌級別
/**
* Get the current log level
*
* @see lavu_log_constants
*
* @return Current log level
*/
int av_log_get_level(void);
日誌輸出函數
/**
* Send the specified message to the log if the level is less than or equal
* to the current av_log_level. By default, all logging messages are sent to
* stderr. This behavior can be altered by setting a different logging callback
* function.
* @see av_log_set_callback
*
* @param avcl A pointer to an arbitrary struct of which the first field is a
* pointer to an AVClass struct or NULL if general log.
* @param level The importance level of the message expressed using a @ref
* lavu_log_constants "Logging Constant".
* @param fmt The format string (printf-compatible) that specifies how
* subsequent arguments are converted to output.
*/
void av_log(void *avcl, int level, const char *fmt, ...)
第一個參數指定該log所屬的結構體,例如AVFormatContext、AVCodecContext等等。
第二個參數指定log的級別,第三個參數爲要輸出的內容。
PS: 結合之前的內容,所謂的輸出log, 其實是設定了具有一定log級別的輸出, 系統會根據作者設置的日誌閾值, 來自動篩選那些日誌是需要輸出的, 那些是不需要輸出的。
自定義log輸出函數
av_log()調用了av_vlog(),av_log()調用了一個函數指針av_log_callback。av_log_callback是一個全局靜態變量,定義如下所示:
static void (*av_log_callback)(void*, int, const char*, va_list) = av_log_default_callback;
從代碼中可以看出,av_log_callback指針默認指向一個函數av_log_default_callback()。av_log_default_callback()即FFmpeg默認的Log函數。
需要注意的是,這個Log函數是可以自定義的。按照指定的參數定義一個自定義的函數後,可以通過FFmpeg的另一個API函數av_log_set_callback()設定爲Log函數。
查看源碼,可以看到 av_log_set_callback() 的聲明如下:
/**
* Set the logging callback
*
* @note The callback must be thread safe, even if the application does not use
* threads itself as some codecs are multithreaded.
*
* @see av_log_default_callback
*
* @param callback A logging function with a compatible signature.
*/
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
從聲明中可以看出,需要指定一個參數爲(void*, int, const char*, va_list),返回值爲void的函數作爲Log函數。
查看av_log_set_callback() 源碼,可以看到此方法只是做了一個函數指針賦值的工作,代碼如下:
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list)) {
av_log_callback = callback;
}
這樣我們可以自定義一個my_logoutput()函數作爲Log的輸出函數:
void selfDefineLogOutPut(void* ptr, int level, const char* fmt,va_list vl){
*******(omit....)
}
編輯好函數之後,使用av_log_set_callback()函數設置該函數爲Log輸出函數即可。
av_log_set_callback(selfDefineLogOutPut);
For Example:
void selfDefineLogOutPut(void* ptr, int level, const char* fmt,va_list vl){
cout << fmt;
return;
}
int main()
{
av_log_set_level(AV_LOG_INFO);
av_log_set_callback(selfDefineLogOutPut);
av_log(nullptr, AV_LOG_ERROR, "My self out put\n");
av_log_get_level();
return 0;
}
輸出結果:
ps: 如果有需要, 還得恢復默認的log輸出行爲, 不要拍拍屁股就走人了:
av_log_set_callback(av_log_default_callback);
把日誌輸出到日誌文件:
大家務必注意,上文的所有騷操作, 一頓操作猛如虎,一看結果250,所有的log都輸出在屏幕上,也即是標準輸出或者標準出錯。怎麼把日誌輸出到log文件裏呢??
這裏就需要結合上一條的操作, 既然能夠自定義log輸出函數, 那就好辦了:
void selfDefineLogOutPut(void* ptr, int level, const char* fmt, va_list vl){
char buff[1024] = "";
sprintf(buff, fmt, vl[0]);
FILE *file = fopen("D:\\ZLJ\\stuff\\a.txt", "wb+");
fwrite(buff, 1, strlen(buff), file);
fclose(file);
return;
}
int main()
{
av_log_set_level(AV_LOG_INFO);
av_log_set_callback(selfDefineLogOutPut);
av_log(nullptr, AV_LOG_ERROR, "My self out put abc = %d \n", 123);
av_log_get_level();
return 0;
}
上面的方法完全可以實現把log輸出到日誌裏。但是,因爲va_list 實在是一個愚蠢到頂的數據結構,不知道那個白癡想出來的可變參數。用起來還是有些不方便。但是有總比沒有要強的多吧,哈哈哈哈