----------------------------------------
author: hjjdebug
date: 2023年 07月 27日 星期四 14:56:38 CST
descriptor: ffmpeg 中 av_log 是怎样工作的?
----------------------------------------
av_log 功能其实只是添加了颜色,LOG级别,及log上下文名称,没有添加时间,函数名称,行号等信息.
就这一点就引起了血雨腥风的代码, 记得我第一次看跟不下去,太长,今天就扼要分析一下:
测试代码:
#include
int main()
{
av_log(NULL,AV_LOG_ERROR,"hello world\n");
return 0;
}
其调用栈
#0 av_log_default_callback (ptr=0x0, level=16, fmt=0x555555556004 "hello world\n", vl=0x7fffffffdbd0) at libavutil/log.c:401
#1 0x00007ffff7d0c2b5 in av_vlog (avcl=0x0, level=16, fmt=0x555555556004 "hello world\n", vl=0x7fffffffdbd0) at libavutil/log.c:432
#2 0x00007ffff7d0c108 in av_log (avcl=0x0, level=16, fmt=0x555555556004 "hello world\n") at libavutil/log.c:411
#3 0x000055555555516c in main () at main.c:5
可变参数为空,所以vl 就不用考虑了. 考察关键函数av_log_default_callback
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
static int print_prefix = 1;
static int count;
static char prev[LINE_SZ]; //LINE_SZ 是1024, prev 是全局变量
AVBPrint part[4]; //打印被分为4个部分, 演示代码比较简单,只关注part[3]即可,其它全为空
char line[LINE_SZ]; //LINE_SZ 是1024
static int is_atty;
int type[2]; // 它代表了part0,part1的颜色,本测试中未用到
unsigned tint = 0;
if (level >= 0) { //AV_LOG_ERROR 值为16
tint = level & 0xff00; //tint 为0, 字体颜色由颜色表决定,否则由tint决定
level &= 0xff; // level 为16
}
if (level > av_log_level) //av_log_level 为全局变量 32
return;
ff_mutex_lock(&mutex); // 上锁,使支持多线程, 这样下面的处理被锁定,只一个线程使用,不会出现乱码
format_line(ptr, level, fmt, vl, part, &print_prefix, type); //这是关键函数,把字符串打印到4个part,2个type
snprintf(line, sizeof(line), "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str); //4个part 合并到line
#if HAVE_ISATTY
if (!is_atty)
is_atty = isatty(2) ? 1 : -1; // is_atty 给1
#endif
if (print_prefix && (flags & AV_LOG_SKIP_REPEATED) && !strcmp(line, prev) &&
*line && line[strlen(line) - 1] != '\r'){ //如果要求跳过重复信息, 就计个数就行,不用重复打印
count++;
if (is_atty == 1)
fprintf(stderr, " Last message repeated %d times\r", count);
goto end;
}
if (count > 0) {
fprintf(stderr, " Last message repeated %d times\n", count);
count = 0;
}
strcpy(prev, line); //把line 保存到prev 变量,供信息是否充分比较用
sanitize(part[0].str); //对part[0].str字符串进行健康检查,对不可打印字符用'?'替代
colored_fputs(type[0], 0, part[0].str); //关键函数, 有level,tint和字符串
sanitize(part[1].str);
colored_fputs(type[1], 0, part[1].str); //处理part[1]
sanitize(part[2].str); //处理part[2]
//NB_LEVELS是8, av_clip是保证level>>8 处于0-7之间的数,此例因level=0x10,level>>3=2,av_clip()后还是2
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[2].str);
sanitize(part[3].str); //处理part[3]
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[3].str);
end:
av_bprint_finalize(part+3, NULL); // 对part+3 finalize
ff_mutex_unlock(&mutex); //释放锁
}
format_line 函数看了一眼,
4个part 都是 AVBPint 对象,是一个buffer打印对象,自己管理自己. 其结构为:
(gdb) ptype part[0]
type = struct AVBPrint {
char *str;
unsigned int len;
unsigned int size;
unsigned int size_max;
char reserved_internal_buffer[1];
char reserved_padding[1000];
}
望文生义也可以理解其意图了.
part0 保存父类名称
part1 保存类名称
part2 保存类型字符串
part3 保存我们要打印的字符串
因为我们的logctx 传的是空,所以part0,part1都为空
因为我们没有设置打印log类型标志,所以part2也为空
static void colored_fputs(int level, int tint, const char *str)
{
int local_use_color;
if (!*str)
return;
if (use_color < 0) //use_color 是全局变量,初始值是-1,所以调用
check_color_terminal(); //通过环境变量及检测,设定use_color=256
if (level == AV_LOG_INFO/8) local_use_color = 0; //AV_LOG_INFO 是不用颜色的0
else local_use_color = use_color; //其它都使用颜色256
ansi_fputs(level, tint, str, local_use_color); //此为真正彩色输出
}
//深入浅出系列, 发现最后出口
static void ansi_fputs(int level, int tint, const char *str, int local_use_color)
{
if (local_use_color == 1) { //系统只支持单色(实际是16色),按此输出
fprintf(stderr,
"\033[%"PRIu32";3%"PRIu32"m%s\033[0m",
(color[level] >> 4) & 15, // 背景
color[level] & 15, // 前景
str);
} else if (tint && use_color == 256) { //系统支持256色, tint为真
fprintf(stderr,
"\033[48;5;%"PRIu32"m\033[38;5;%dm%s\033[0m",
(color[level] >> 16) & 0xff, //背景
tint, //前景由tint指定
str);
} else if (local_use_color == 256) { //系统支持256色, tint为假
fprintf(stderr,
"\033[48;5;%"PRIu32"m\033[38;5;%"PRIu32"m%s\033[0m",
(color[level] >> 16) & 0xff, //背景由颜色表高位决定, 本测试走的是这路,为0
(color[level] >> 8) & 0xff, // 前景由颜色表次高位决定, 为0xc4
str);
} else
fputs(str, stderr); //无颜色按fputs 输出, 对应AV_LOG_INFO
}
(gdb) p/x color // 关注前8个是log 颜色表
$36 = {0x34c441, 0xd041, 0xc411, 0xe203, 0xfd09, 0x2802, 0x2202, 0x2207, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfa09, 0xdb15, 0xc905, 0xd515, 0xcf05, 0x3316, 0x2706, 0x9b12, 0xc014, 0x9914, 0x9314, 0x0
本测试AV_LOG_ERROR=0x10, level=0x10/2=2, 所以color[level]=0xc411