在Visual Studio 開發(二):VS 2017配置FFmpeg開發環境 一文中,我們配置好了FFmpeg的開發環境,下面我們開始邊實戰,邊學習FFmpeg。
首先,我們要學習的就是FFmpeg的日志輸出系統 。
一、FFmpeg 日志輸出系統介紹
FFmpeg 日志輸出的核心函數方法為: av_log() 。為什么說av_log()是FFmpeg中輸出日志的核心函數函數?
因為我們隨便打開一個FFmpeg的源代碼文件,就會發現其中遍布着av_log()函數。一般情況下FFmpeg類庫的源代碼不允許使用printf()這種函數,所有的輸出一律使用的av_log()。
二、av_log() 函數說明
av_log()的聲明位於libavutil\log.h,具體的聲明代碼如下:
/** * 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. * @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, ...) av_printf_format(3, 4);
其中第一個參數指定該log所屬的結構體,例如AVFormatContext、AVCodecContext等等。第二個參數指定log的級別,第三個參數為要輸出的內容,源代碼中定義了如下幾個級別:
/** * 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
從定義中可以看出來,av_log()的日志級別分別是:
AV_LOG_PANIC,AV_LOG_FATAL,AV_LOG_ERROR,AV_LOG_WARNING,AV_LOG_INFO,AV_LOG_VERBOSE,AV_LOG_DEBUG。
每個級別定義的數值代表了嚴重程度,數值越小代表越嚴重。
默認av_log()輸出的級別是AV_LOG_INFO。
三、設置日志輸出等級
在上面,我們講到av_log()函數是可以設置日志的內容的等級的。而對於輸出的日志內容,我們也是可以設置等級的。FFmpeg提供了av_log_set_level()用於設置當前Log的級別。
函數聲明如下:
/** * 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的級別。
四、日志輸出實戰
通過下面的代碼,我們就可以理解上面講的日志輸出及設置日志輸出等級的邏輯了。
#include "pch.h" #include <iostream> extern "C"{ #include "libavutil/log.h" } int main(int argc, char* argv[]) { av_log_set_level(AV_LOG_ERROR); av_log(NULL, AV_LOG_INFO, "Hello World\n"); return 0; }
五、自定義FFmpeg日志輸出
從文章開頭的函數調用圖可以看到,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 my_logoutput(void* ptr, int level, const char* fmt,va_list vl){ ****(省略....) }
編輯好函數之后,使用av_log_set_callback()函數設置該函數為Log輸出函數即可。
av_log_set_callback(my_logoutput);
下面是自定義日志輸出的實例源碼:
#include "pch.h" #include <iostream> extern "C"{ #include "libavutil/log.h" } void my_logoutput(void* ptr, int level, const char* fmt, va_list vl) { printf("Hello Log Output! Content = %s", fmt); } int main(int argc, char* argv[]) { av_log_set_callback(my_logoutput); // 設置自定義的日志輸出方法 av_log(NULL, AV_LOG_INFO, "Hello World\n"); return 0; }
輸出如下:
附:本文涉及C語言知識點 --> 函數指針。