前言
上一篇博客介绍了怎么控制FFmpeg输出的调试打印信息,本文简单总结下源码中对应的关键实现,并提供了升级版的打印函数,能打印文件名/行号/函数名等信息。
一、FFmpeg的打印函数av_log
FFmpeg使用自定义的av_log进行打印,函数实现在源码libavutil/log.c文件中,能够达到的效果包括:
1、颜色显示:不同打印等级对应不同的颜色,一目了然;
2、日志分级:通过设定打印等级,可以管理输出的日志信息。
av_log函数的调用关系如下,可见最终还是调用了我们熟悉的打印函数fprintf、fputs。
├── av_log
│ ├── av_vlog
│ ├── av_log_callback
│ ├── av_log_default_callback
│ ├── format_line
│ ├── colored_fputs
│ ├── ansi_fputs
│ ├── fprintf
│ ├── fputs
二、av_log的具体实现
1.颜色显示
代码如下:
static void ansi_fputs(int level, int tint, const char *str, int local_use_color)
{
//16色方案
if (local_use_color == 1) {
fprintf(stderr,
"\033[%"PRIu32";3%"PRIu32"m%s\033[0m",
(color[level] >> 4) & 15,
color[level] & 15,
str);
}
//256色方案
else if (tint && use_color == 256) {
fprintf(stderr,
"\033[48;5;%"PRIu32"m\033[38;5;%dm%s\033[0m",
(color[level] >> 16) & 0xff,
tint,
str);
} else if (local_use_color == 256) {
fprintf(stderr,
"\033[48;5;%"PRIu32"m\033[38;5;%"PRIu32"m%s\033[0m",
(color[level] >> 16) & 0xff,
(color[level] >> 8) & 0xff,
str);
} else
fputs(str, stderr);
}
这里涉及printf输出增加颜色的基础知识,有几篇参考博客介绍的很详细。
printf 彩色输出:16色输出
printf输出到终端的颜色设定:涉及256色输出
颜色(ASCII color):涉及256色输出
此处引用参考文献图片并总结如下,其中:
16色
典型用法为
printf("\033[1;31;40m %s \033[0m", "red"); //红色
printf("\033[1;32;40m %s \033[0m", "green"); //绿色
printf("\033[1;33;40m %s \033[0m", "yellow"); //黄色
256色
典型用法为:
//具体背景的160和前景的231对应什么颜色,在第3篇博客里有张色卡可以查
printf("\033[48;5;160m\033[38;5;231m背景前景修改ABCDE\033[0;0m");
2.日志分级
代码如下:
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
//打印等级大于av_log_level,直接return,这就不会输出打印了
if (level > av_log_level)
return;
//中间省略
//按照打印格式组织数据
format_line(ptr, level, fmt, vl, part, &print_prefix, type);
//中间省略
//调用colored_fputs函数打印
colored_fputs(type[0], 0, part[0].str);
colored_fputs(type[1], 0, part[1].str);
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[2].str);
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[3].str);
//中间省略
}
其中,av_log_level是一个全局静态变量,默认等级是AV_LOG_INFO
static int av_log_level = AV_LOG_INFO;
//获取当前打印等级
int av_log_get_level(void)
{
return av_log_level;
}
//设置当前打印等级
void av_log_set_level(int level)
{
av_log_level = level;
}
显而易见,ffmpeg工具会通过解析命令行获取loglevel参数,进而修改代码中av_log_level的值,实现控制打印等级的目的。
三、av_log魔改版
1.打印时能显示文件名、行号、函数名
在源码libavutil/log.h中找到av_log的声明如下:
/**
* 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);
新增宏定义如下
#define MY_AV_LOG(avcl, level, fmt, args, ...) av_log(avcl, level, "[%12s|%12s|%4d]"fmt, __FILE__, __LINE__, __func__, ##args)
将代码中调用av_log打印的地方,使用MY_AV_LOG替换即可,如:
MY_AV_LOG(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
总结
本文记录了FFmpeg源码中日志分级打印的实现方式,并提供了升级版的打印函数,可输出文件名/行号/函数名等信息。调试源码时能根据打印信息定位到具体位置,可以说是相当方便了。