----------------------------------------
 author: hjjdebug
 date:   2023年 07月 27日 星期四 14:56:38 CST
 descriptor: ffmpeg 中 av_log 是怎样工作的?
 ----------------------------------------
 av_log 功能其实只是添加了颜色,LOG级别,及log上下文名称,没有添加时间,函数名称,行号等信息.
 就这一点就引起了血雨腥风的代码, 记得我第一次看跟不下去,太长,今天就扼要分析一下:
 测试代码:
#include <libavutil/avutil.h>
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 <repeats 29 times>, 0xd515, 0xcf05, 0xd515, 0xcf05, 0xd515, 0xcf05}
 本测试AV_LOG_ERROR=0x10, level=0x10/2=2, 所以color[level]=0xc411