关于ffmpeg:音视频系列三ffmpeg之日志打印

title: 音视频系列三:ffmpeg之日志打印
categories:[ffmpeg]
tags:[音视频编程]
date: 2021/11/27

<div align = ‘right’>作者:hackett</div>

<div align = ‘right’>微信公众号:加班猿</div>

在上一篇 Visual Studio2019集成ffmpeg之hello world中,咱们曾经配置好visual studio的开发环境,接下来持续依据上一篇的环境来学习ffmpeg的日志打印;

日志打印对于定位问题或者寻找bug都起到决定性的作用。

一、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 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, ...) 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

/**
 * Extremely verbose debugging, useful for libav* development.
 */
#define AV_LOG_TRACE    56

#define AV_LOG_MAX_OFFSET (AV_LOG_TRACE - AV_LOG_QUIET)

从定义中能够看进去,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_TRACE。

每个级别定义的数值代表了重大水平,数值越小代表越重大。

默认av_log()输入的级别是AV_LOG_INFO。

三、设置/获取日志输入等级

在下面,咱们讲到av_log()函数是能够设置日志的内容的等级的。而对于输入的日志内容,咱们也是能够设置等级的。FFmpeg提供了av_log_set_level()用于设置以后Log的级别,av_log_get_level用于获取以后Log的级别。

av_log_set_level函数申明如下:

/**
 * Set the log level
 *
 * @see lavu_log_constants
 *
 * @param level Logging level
 */
void av_log_set_level(int level);

av_log_set_level函数申明如下:

/**
 * Get the current log level
 *
 * @see lavu_log_constants
 *
 * @return Current log level
 */
int av_log_get_level(void);

四、日志输入实战

通过上面的代码,咱们就能够了解下面讲的日志输入及设置日志输入等级的逻辑了。

#include <iostream>

extern "C"{
#include "libavutil/log.h"
}

int main(int argc, char* argv[]) {
    av_log_set_level(AV_LOG_DEBUG); //设置日志级别
    av_log(NULL, AV_LOG_DEBUG, "hello world log"); //打印日志
    printf("av_log_get_level: %d\n",av_log_get_level());
    system("pause"); //窗口期待
    return 0;
}

五、自定义FFmpeg日志输入

查看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_log_printf(void* ptr, int level, const char* fmt,va_list vl){
    省略...
}

编辑好函数之后,应用av_log_set_callback()函数设置该函数为Log输入函数即可。

av_log_set_callback(my_log_printf);

上面是自定义日志输入的实例源码:

#include<iostream>
using namespace std;

extern "C" {//蕴含C头文件
#include "libavutil/log.h" 
};

void my_log_printf(void* ptr, int level, const char* fmt, va_list vl) {
    printf("my_log_printf enter! Content : %s\nlog level : %d\n", fmt, level);
}

int main(int argc, char* argv[]) {

    av_log_set_level(AV_LOG_DEBUG); //设置日志级别
    av_log_set_callback(my_log_printf);  // 设置自定义的日志输入办法
    av_log(NULL, AV_LOG_DEBUG, "hello world log"); //打印日志

    system("pause"); //窗口期待
    return 0;
}

如果你感觉文章还不错,能够给个”三连

我是加班猿,咱们下期见

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理