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;
}
如果你感觉文章还不错,能够给个 ”三连“
我是 加班猿,咱们下期见