使用线程局部存储解决ffmpeg中多实例调用下自定义日志回调问题

ops/2024/11/25 2:31:28/

1 问题描述

最近在封装一个库,调用方传入一个URL及对应的回调后就开始执行ffmpeg拉流+硬解码+硬件格式转换,并将得到的数据帧通过回调传递给调用方;除了数据帧回调外,还有日志回调用来传递一些调试信息。

因为该封装库可能被一个进程调用多次(如存在多个小窗口同时播放多个视频),只要在日志回调中包含调用方唯一标识即可进行区分,对于我们自己的日志输出可以这样做,但是发现ffmpeg内部的一些日志如av_dump_format打印却不行,因为没有额外的信息来传递调用方标识,最后发现可以使用线程局部存储来解决这个问题,在此进行记录。

2 问题解决

为了获得av_dump_format的日志输出,我们首先使用av_log_set_callback来对ffmpeg里面的日志进行接管,但是av_dump_format里面打印日志时无法传递任何标识。

这里我们发现av_dump_format是在process函数中调用的,他们一定属于同一个线程,所以可以通过定义一个全局的线程局部变量,并在process函数开始设置为当前调用的上下文,之后就可以在av_dump_format的日志回调中使用这个线程局部变量来获取当前调用的上下文,这样就实现了区分多实例的目的。

其中线程局部变量使用c++11的thread_local来实现,相关代码如下。

static thread_local DecData *thread_local_data = NULL; /*这样可以区分多个实例的使用*/// 接收ffmpeg内部的日志,当前仅仅显示小于info级别的日志
static void ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list vl) {if (level > AV_LOG_INFO) {return;}int log_size = vsnprintf(NULL, 0, fmt, vl) + 1;char * buf = (char*)malloc(log_size);vsnprintf(buf, log_size, fmt, vl);if (thread_local_data) {thread_local_data->config.log_cb(buf);}free(buf);
}int process(DecData*  data) {// ffmpegAVFormatContext *fmt_ctx = NULL;int video_stream_index = 0;int audio_stream_index = 0;AVStream* video_stream = NULL;AVStream* audio_stream = NULL;AVDictionary *opt = NULL;int ret = 0;AVPacket *pkt = NULL;AVStream* out_video_stream = NULL;AVStream* out_audio_stream = NULL;AVBSFContext * bst_ctx = NULL;AVPacket *bst_pkt = NULL;const char *name;ConfigInfo *config = &data->config;// 自定义ffmpeg日志回调函数thread_local_data = data;av_log_set_callback(ffmpeg_log_callback);// 如果是rtsp或rtsps则使用rtsp over tcpif (strncmp(config->pull_url, "rtsp://", 7) == 0 || strncmp(config->pull_url, "rtsps://", 8) == 0) {av_dict_set(&opt, "rtsp_transport", "tcp", 0);}// 如果是网络流,则设置i/o超时时间av_dict_set(&opt, "timeout", "5000000", 0);// 打开输入文件ret = avformat_open_input(&fmt_ctx, config->pull_url, NULL, &opt);if (ret != 0) {logging(data, "avformat_open_input error %d\n", ret);goto end;}av_dict_free(&opt);fmt_ctx->opaque = data;// 获取输入文件信息ret = avformat_find_stream_info(fmt_ctx, NULL);if (ret != 0) {logging(data, "avformat_find_stream_info error %d\n", ret);goto end;}// 打印输入文件信息av_dump_format(fmt_ctx, 0, config->pull_url, 0);………
}


http://www.ppmy.cn/ops/136451.html

相关文章

常用Rust日志处理工具教程

在本文中,我想讨论Rust中的日志。通过一些背景信息,我将带您了解两个日志库:env_logger和log4rs。最后,我将分享我的建议和github的片段。 Rust log介绍 log包是Rust中日志API的事实标准,共有五个日志级别&#xff1…

Linux环境开启MongoDB的安全认证

在Linux环境下启动MongoDB并启用安全认证,你需要进行以下步骤: 1.创建MongoDB的配置文件(如果还没有的话)。 2.编辑配置文件以启用认证。 3.重启MongoDB服务。 以下是具体步骤的示例: 创建MongoDB配置文件&#xf…

基于YOLOv8深度学习的农作物番茄成熟度检测系统研究与实现(PyQt5界面+数据集+训练代码)

本研究详细研究并实现了一种基于深度学习的农作物番茄成熟度检测系统,旨在为现代农业提供智能化、自动化的解决方案。随着农业智能化的发展,传统的农作物成熟度检测方法面临着效率低、依赖人工经验等问题,而本系统通过结合先进的YOLOv8目标检…

Python网络爬虫技术及其应用

Python网络爬虫技术及其应用 在当今数字化时代,互联网已经成为信息传播的主要渠道。海量的数据每天都在互联网上产生,这些数据对于企业决策、市场分析、科学研究等有着极其重要的价值。然而,如何高效地收集并利用这些数据成为了一个挑战。Py…

【MySQL数据库】C#实现MySQL数据库最简单的查询和执行函数

文章目录 前言一、查询方法二、执行方法 前言 C#和MySQL数据库是常见的数据交互,标准的查询和执行方法如下,做个记录。 一、查询方法 private static int QueryTable(string tableName, DateTime today, string stepName){int result 0; // 返回数据…

用Python爬虫“偷窥”1688商品详情:一场数据的奇妙冒险

引言:数据的宝藏 在这个信息爆炸的时代,数据就像是一座座等待挖掘的宝藏。而对于我们这些电商界的探险家来说,1688上的商品详情就是那些闪闪发光的金子。今天,我们将化身为数据的海盗,用Python这把锋利的剑&#xff0…

ROS VRRP软路由双线组网方式

虚拟路由冗余协议 Virtual Router Redundancy Protocol (VRRP),MikroTik RouteROS VRRP 协议遵循 RFC 2338。 VRRP 协议是保证访问一些资源不会中断,即通过多台路由器组成一个网关集合,如果其中一台路由器出现故障,会自动启用另外…

c++版opencv长文指南

c版opencv长文指南 1、配置opencv库1.1 下载1.2 配置1.2.1 配置包含目录1.2.2 配置库含目录1.2.3 配置链接器1.2.4 配置系统环境变量 2、学习路线3、入门知识3.1 图像读取与显示3.2 图像色彩空间转换3.3 图像对象的创建与赋值3.3.1 图像对象的创建3.3.2 图像对象的赋值 3.4 图像…