• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

ffmpeg av_log 是怎样工作的?

武飞扬头像
hjjdebug
帮助1

----------------------------------------
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

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfjjcag
系列文章
更多 icon
同类精品
更多 icon
继续加载