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
相关文章:
ffmpeg 中 av_log 是怎样工作的?
---------------------------------------- author: hjjdebug date: 2023年 07月 27日 星期四 14:56:38 CST descriptor: ffmpeg 中 av_log 是怎样工作的? ---------------------------------------- av_log 功能其实只是添加了颜色,LOG级别,及log上下文名称,没有添加时间,函…...

HTML+CSS+JavaScript:轮播图自动播放
一、需求 轮播图如下图所示,需求是每隔一秒轮播图自动切换一次 二、代码素材 以下是缺失JS部分的代码,感兴趣的小伙伴可以先自己试着写一写 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /&…...

python 自动化数据提取之正则表达式
>>>> 前 言 我们在做接口自动化的时候,处理接口依赖的相关数据时,通常会使用正则表达式来进行提取相关的数据,今天在这边和大家聊聊如何在python中使用正则表达式。 正则表达式,又称正规表示式、正规表示法、正规…...

分布式事务之本地事务
🚀 分布式事务 🚀 🌲 AI工具、AI绘图、AI专栏 🍀 🌲 如果你想学到最前沿、最火爆的技术,赶快加入吧✨ 🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆&…...
PyTorch 初级教程:构建你的第一个神经网络
PyTorch 是一个在研究领域广泛使用的深度学习框架,提供了大量的灵活性和效率。本文将向你介绍如何使用 PyTorch 构建你的第一个神经网络。 一、安装 PyTorch 首先,我们需要安装 PyTorch。PyTorch 的安装过程很简单,你可以根据你的环境&…...

SpringBoot使用MyBatis Plus + 自动更新数据表
1、Mybatis Plus介绍 Mybatis,用过的都知道,这里不介绍,mybatis plus只是在mybatis原来的基础上做了些改进,增强了些功能,增强的功能主要为增加更多常用接口方法调用,减少xml内sql语句编写,也可…...
【设计模式】简单工厂模式
C语言实现简单的工厂模式 #include <stdio.h> #include <stdlib.h>// 图形类型枚举 typedef enum {CIRCLE,SQUARE,RECTANGLE } ShapeType;// 图形结构体 typedef struct {ShapeType type;float area; } Shape;// 创建圆形 Shape* createCircle() {Shape* circle …...
推荐系统-ALS协同过滤算法实现
从协同过滤的分类来说,ALS(Alternating Least Squares,交替最小二乘)算法属于User-Item CF,也叫做混合CF,它同时考虑了User和Item两个方面,通过数量相对少的未被观察到的隐藏因子,来…...

QT第三讲
思维导图 蜡笔小新闹钟 需求: 实现 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTime> //时间类 #include<QTimerEvent> //事件处理类 #include<QtTextToSpeech> //文本转语音类 #include<QMessageBo…...

Linux内核的I2C驱动框架详解------这应该是我目前600多篇博客中耗时最长的一篇博客
目录 1 I2C驱动整体框架图 2 I2C控制器 2.1 I2C控制器设备--I2C控制器在内核中也被看做一个设备 2.2 i2c控制器驱动程序 2.3 platform_driver结构体中的probe函数做了什么 2.3.1 疑问: i2cdev_notifier_call函数哪里来的 2.3.2 疑问:为什么有两…...

【点云处理教程】05-Python 中的点云分割
一、说明 这是我的“点云处理”教程的第 5 篇文章。“点云处理”教程对初学者友好,我们将在其中简单地介绍从数据准备到数据分割和分类的点云处理管道。 在上一教程中,我们看到了如何过滤点云以减少噪声或其密度。在本教程中,我们将应用一些聚…...

代码随想录算法训练营之JAVA|第十七天| 654. 最大二叉树
今天是第17天刷leetcode,立个flag,打卡60天。 算法挑战链接 654. 最大二叉树https://leetcode.cn/problems/maximum-binary-tree/description/ 第一想法 错误的想法,就不说了。 看完代码随想录之后的想法 用递归模拟真实的过程 如果我…...
C++重写函数、隐藏函数、重载函数的区别对比
目录 1.函数重载 1.1定义 1.2函数重载的规则: 1.3函数重载的作用: 2.函数重写: 2.1定义 2.2例子: 3.函数隐藏 3.1定义 3.2举个例子: 1.函数重载 1.1定义 我们在学类和对象的封装特性时学过一个词叫重载,…...
15.python设计模式【函数工厂模式】
1.知识讲解 内容:定义一个字典,在python中一切皆对象,将所有的函数进行封装,然后定一个分发函数进行分发,将原来if…else全部干掉。角色: 函数(function)函数工厂(funct…...

Redis主从复制、哨兵、cluster集群原理+实验
目录 一、Redis 主从复制 1、主从复制的作用 2、主从复制流程 3、搭建Redis 主从复制 安装Redis(所有主机) 修改Master节点Redis配置文件 修改Slave节点Redis配置文件 验证主从效果 一、Redis 主从复制 主从复制,是指将一台Redis服务器的数据&am…...
微信小程序如何实现页面传参?
前言 只要你的小程序超过一个页面那么可能会需要涉及到页面参数的传递,下面我总结了 4 种页面方法。 路径传递 通过在url后面拼接参数,参数与路径之间使用 ? 分隔,参数键与参数值用 相连,不同参数用 & 分隔;如…...

OPC DA 客户端与服务器的那点事
C#开发OPC客户端,使用OPCDAAuto.dll。在开发过程中偶遇小坎坷,主要记录一下问题解决办法。 1、建立客户端,参考链接。建立WinFrom工程,将博客中代码全部复制即可运行: https://www.cnblogs.com/kjgagaga/p/17011730.…...

Java 错误异常介绍(Exceptions)
1、异常介绍 异常是程序执行期间发生的意外事件。它影响程序指令流,从而导致程序异常终止。 发生异常的原因有很多。其中包括: 无效的用户输入 设备故障 网络连接丢失 物理限制(磁盘内存不足) 代码错误 打开一个不可用的文…...
每日一题——旋转数组的最小数字
题目 有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这…...
SpringBoot Jackson 日期格式化统一配置
目录 1.在全局配置文件配置 2.通过JavaBean方式配置 1.在全局配置文件配置 spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT8 该配置方式仅支持 Date 类型的日期格式化,不支持LocalDate 及 LocalDateTime 的格式化。 2.通过JavaBean方式配置 …...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...