FFmpeg開發實戰(一):FFmpeg 打印日志


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語言知識點 --> 函數指針。

 


免責聲明!

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



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