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

embedded/2024/11/25 15:47:22/

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/embedded/140419.html

相关文章

Elasticsearch面试内容整理-高级特性

Elasticsearch 提供了一系列高级特性,这些特性可以极大地增强其搜索、分析和管理能力,使得它在大数据场景中表现出色。以下是 Elasticsearch 的一些重要高级特性: 近实时搜索(Near Real-Time Search) Elasticsearch 的一个关键特性是 近实时搜索(NRT),这意味着数据写入…

Java 8 常用特性 (JDK1.8)

Oracle 于 2014 发布了 Java8(jdk1.8),诸多原因使它成为目前市场上使用最多的 jdk 版本。虽然发布距今已将近 7 年,但很多程序员对其新特性还是不够了解 为了不脱离队伍太远,还是有必要对这些新特性做一些总结梳理。它…

Jdk1.8新特性

新增的类以及常用的方法 在Java的java.util.concurrent包中,除了之前提到的并发工具类外,还有一些新增的类以及它们常用的方法。以下是一些例子: 新增的类 ‌CompletableFuture‌: CompletableFuture是Java 8中引入的一个类&a…

SQL递归查询树结构语法

对于树形结构数据,经常会有递归查询的需求,语法如下: 注意替换sql中的表名和id入参 mysql WITH RECURSIVE node_cte (id, parent_id) AS (SELECT id, parent_id FROM my_table -- 表名,换成要查询的名称WHERE id :id -- 根节点…

二分法(折半法)查找【有动图】

二分法,也叫做折半法,就是一种通过有序表的中间元素与目标元素进行对比,根据大小关系排除一半元素,然后继续在剩余的一半中进行查找,重复这个过程直至找到目标值或者确定目标值不存在。 我们从结论往回推,…

Java中的关键字 native

Java中的关键字 native 在Java中,native 关键字用于声明本地方法,即由非Java语言(如C、C)编写的方法。这些本地方法可以通过Java调用,以便访问底层操作系统、硬件资源或遗留代码库。使用 native 关键字的主要目的是让…

Spring Security 中的 AuthenticationProvider接口(验证认证请求)

本篇博客将教您如何在 Spring Security 中使用 AuthenticationProvider 来验证不同的认证逻辑,并展示如何创建自定义的 AuthenticationProvider。 AuthenticationProvider 的作用 AuthenticationProvider 是 Spring Security 中的一个接口,封装了认证逻…

【jvm】为什么要用元空间替代永久代

目录 1. 说明2. 永久代的限制与问题2.1 内存管理限制2.2 垃圾收集效率2.3 类的卸载问题 3. 元空间的优势 1. 说明 1.Java使用元空间替代永久代,这一变化主要源于永久代在实现上存在的限制和问题,以及元空间所提供的更优性能和更高灵活性。2.Java使用元空…