ffmpeg 中 av_log 是怎样工作的?

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/15578.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工程师是怎样对待开源

工程师如何对待开源 本文是笔者作为一个在知名科技企业内从事开源相关工作超过 20 年的工程师&#xff0c;亲身经历或者亲眼目睹很多工程师对待开源软件的优秀实践&#xff0c;也看到了很多 Bad Cases&#xff0c;所以想把自己的一些心得体会写在这里&#xff0c;供工程师进行…

跨域冲突问题解决

1、问题分析 服务端和nginx代理 都做了跨域配置&#xff0c;导致请求头重复 headers 里面有两个重复的 Access-Control-Allow-Origin Access-Control-Allow-Methods Access-Control-Allow-Credentials 2、解决方法 在nginx去除服务端的跨域配置 proxy_hide_header Access-…

铁路关基保护新规:优先采购安全可信的网络产品和服务!

《征求意见稿》第十四条提到&#xff1a;运营者应当加强供应链安全保护&#xff0c;优先采购安全可信的网络产品和服务&#xff1b;采购网络产品和服务影响或者可能影响国家安全的&#xff0c;运营者应当预判网络产品和服务投入使用后可能带来的国家安全风险&#xff0c;按照国…

quarkus核心编程笔记

此篇只做总结&#xff0c;有大佬做的更详细 大佬quarkus笔记 依赖注入 在应用中&#xff0c;一个接口有多个实现是很常见的&#xff0c;那么依赖注入时&#xff0c;如果类型是接口&#xff0c;如何准确选择实现呢&#xff1f; 修饰符匹配Named注解属性匹配根据优先级选择写…

gitlab api获取用户星标项目

获取所有项目 gitlab_url: gitlab的地址 token: 管理员账号的access_token https://gitlab_url/api/v4/projects/?private_tokentoken&page1&per_page20注&#xff1a;该链接只获取一页的数据&#xff0c;可通过循环累加page遍历所有项目获取某个用户的星标项目 参考…

论文笔记--GloVe: Global Vectors for Word Representation

论文笔记--GloVe: Global Vectors for Word Representation 1. 文章简介2. 文章概括3 文章重点技术3.1 两种常用的单词向量训练方法3.2 GloVe3.3 模型的复杂度 4. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;GloVe: Global Vectors for Word Representa…

自己实现MyBatis 底层机制--抽丝剥茧(上)

&#x1f600;前言 本篇博文是学习过程中的笔记和对于MyBatis底层机制的分析思路&#xff0c;希望能够给您带来帮助&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到…

WPF实战学习笔记32-登录、注册服务添加

增加全局账户名同步 增加静态变量 添加文件&#xff1a;Mytodo.Common.Models.AppSession.cs ausing Prism.Mvvm; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; us…

(树) 剑指 Offer 27. 二叉树的镜像 ——【Leetcode每日一题】

❓剑指 Offer 27. 二叉树的镜像 难度&#xff1a;简单 请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像。 例如输入&#xff1a; 4/ \2 7/ \ / \1 3 6 9镜像输出&#xff1a; 4/ \7 2/ \ / \9 6 3 1示例 1&#xff1a; 输…

28_计算机网络(Computer Networks)基础

本篇介绍计算机网络的基础知识。 文章目录 1. 计算机网络历史2. 以太网" (Ethernet)2.1 以太网" (Ethernet)的简单形式及概念2.2 指数退避解决冲突问题2.3 利用交换机减少同一载体中设备2.4 互联网&#xff08;The Internet&#xff09;2.5 路由(routing)2.6 数据包…

基于峰谷分时电价引导下的电动汽车充电负荷优化(matlab代码)

目录 1 主要内容 峰谷电价优化 电动汽车充电负荷变化 2 部分代码 3 程序结果 1 主要内容 该程序基本复现《基于峰谷分时电价引导下的电动汽车充电负荷优化》&#xff0c;代码主要做的是基于NSGA-II的电动汽车充电负荷优化&#xff0c;首先&#xff0c;在研究电动汽车用户充…

重生之我要学C++第五天

这篇文章主要内容是构造函数的初始化列表以及运算符重载在顺序表中的简单应用&#xff0c;运算符重载实现自定义类型的流插入流提取。希望对大家有所帮助&#xff0c;点赞收藏评论&#xff0c;支持一下吧&#xff01; 目录 构造函数进阶理解 1.内置类型成员在参数列表中的定义 …

Webpack5 多线程Threads

文章目录 一、Threads 是什么&#xff1f;二、为什么使用 Threads&#xff1f;三、怎么使用 Threads&#xff1f;注意事项结论 一、Threads 是什么&#xff1f; Threads 是指在计算机领域中&#xff0c;指的是操作系统分配给处理器执行任务的最小单位。在Webpack5中&#xff0…

【Matlab】基于BP神经网络的数据回归预测新数据(Excel可直接替换数据)

【Matlab】基于BP神经网络的数据回归预测新数据(Excel可直接替换数据) 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码5.1 main.m5.2 NewData.m6.完整代码6.1 main.m6.2 NewData.m7.运行结果1.模型原理 基于BP神经网络的数据回归预测是一种常见的机器学习方法,用于处…

【云原生】Docker容器命令监控+Prometheus监控平台

目录 1.常用命令监控 docker ps docker top docker stats 2.weave scope 1.下载 2.安装 3.访问查询即可 3.Prometheus监控平台 1.部署数据收集器cadvisor 2.部署Prometheus 3.部署可视化平台Gragana 4.进入后台控制台 1.常用命令监控 docker ps [rootlocalhost ~…

重新审视MHA与Transformer

本文将基于PyTorch源码重新审视MultiheadAttention与Transformer。事实上&#xff0c;早在一年前博主就已经分别介绍了两者&#xff1a;各种注意力机制的PyTorch实现、从零开始手写一个Transformer&#xff0c;但当时的实现大部分是基于d2l教程的&#xff0c;这次将基于PyTorch…

opencv顺时针,逆时针旋转视频并保存视频

原视频 代码 import cv2# 打开视频文件 video cv2.VideoCapture(inference/video/lianzhang.mp4)# 获取原视频的宽度和高度 width int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))# 创建视频编写器并设置输出视频参数 fourcc …

YAML+PyYAML笔记 4 | YAML字符流、节点属性、块伸缩标头使用

4 | YAML字符流、节点属性、块伸缩标头使用 1 字符流1.1 表示方式1.2 字符流解析 2 节点属性3 块伸缩标头 1 字符流 1.1 表示方式 YAML字符流是将多个文档放在同一个文件中&#xff0c;通过“—”分隔符进行分割&#xff1b;示例&#xff1a; --- user1:name: xiaomingage: …

【C++】类和对象(下)

1、初始化列表 初始化列表&#xff1a;以一个冒号开始&#xff0c;接着是一个以逗号分隔的数据成员列表&#xff0c;每个"成员变量"后面跟一个放在括号中的初始值或表达式。 class Date { public:Date(int year, int month, int day): _year(year), _month(month), _…

OSPF协议RIP协议+OSPF实验(eNSP)

本篇博客主要讲解单区域的ospf&#xff0c;多区域的仅作了解。 目录 一、OSPF路由协议概述 1.内部网关协议和外部网关协议 二、OSPF的应用环境 1.从以下几方面考虑OSPF的使用 2.OSPF的特点 三、OSPF重要基本概念 3.1&#xff0c;辨析邻居和邻接关系以及七种邻居状态 3…