多媒体库SDL以及实时音视频库WebRTC中的多线程问题实战详解

news2024/11/25 4:26:40

目录

1、概述

2、开源跨平台多媒体库SDL介绍

3、开源音视频实时通信库WebRTC介绍

4、在国产化Linux桌面系统中遇到的SDL多线程问题

5、在给WebRTC新增外部音频插件库时遇到的多线程问题

6、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具案例集锦(专栏文章正在更新中...)https://blog.csdn.net/chenlycly/category_12279968.html       在音视频项目中使用到了开源跨平台多媒体库SDL和开源音视频实时通信库WebRTC,在使用过程中遇到了一类多线程问题,依据项目问题的实战排查过程,对这类问题进行一个总结,给大家提供一个借鉴或参考。

1、概述

       开源跨平台多媒体库SDL,因为其良好的跨平台特性和功能封装,被广泛地应用于游戏开发、模拟器、媒体播放器、视频会议等多个应用领域中。

       开源音视频实时通信库WebRTC因为其较好的音视频效果及良好的网络适应性,目前已被广泛的应用到视频会议、实时音视频直播等领域中。在视频会议领域,腾讯会议、华为WeLink、字节飞书、阿里钉钉、小鱼易连均提供了基于WebRTC方案的视频会议。

        使用这些开源库的应用程序运行在各式各样的系统环境中,不可避免地会出现这样或那样的问题。本文将详细讲述使用SDL和WebRTC过程遇到的两例典型的线程约束问题。

2、开源跨平台多媒体库SDL介绍

       SDL(Simple DirectMedia Layer)是一套开源的跨平台多媒体开发库,使用 C 语言写成。它提供了绘制图像、播放声音、获取键盘输入等相关的 API,大大降低多媒体应用开发难度的同时,也让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。多用于开发游戏、模拟器、媒体播放器、音视频播放等多个应用领域。

SDL 有两个常见版本:SDL1.2 和 SDL2.x。在不支持 OpenGL ES2 的嵌入式平台上,只能使用 SDL1.2,SDL2.x 依赖 OpengGL ES2。

       在音视频开发领域,主要使用SDL去实现音视频的播放。SDL底层根据不同平台采用不同的渲染技术,比如在Windows平台上使用DirectX去渲染,在Linux平台上则依赖OpenGL去显示。

       在Windows平台上我们可以去操作Windows平台专用的DirectX库中D3D9或D3D11去绘制视频图像,在Linux国产化桌面系统中则使用SDL库去进行视频图像的渲染与绘制(Linux平台上SDL底层使用的是OpenGL)。

3、开源音视频实时通信库WebRTC介绍

        WebRTC(Web Real-Time Communication)是一个由Google发起的实时音视频通讯C++开源库,其提供了音视频采集、编码、网络传输,解码显示等一整套音视频解决方案,我们可以通过该开源库快速地构建出一个音视频通讯应用。

一个实时音视频应用软件一般都会包括这样几个环节:音视频采集、音视频编码(压缩)、前后处理(美颜、滤镜、回声消除、噪声抑制等)、网络传输、解码渲染(音视频播放)等。每一个细分环节,还有更细分的技术模块。

       WebRTC的起源,要从2010年Google以6820万美元收购VoIP软件开发商Global IP Solutions的 GIPS引擎谈起,在经过收购之后没多久,Google将该引擎改名为“WebRTC”,并宣布向全球开发者开源。

       WebRTC项目最开始是让Web开发者能够基于浏览器(Chrome\FireFox\...)轻易快捷开发出丰富的实时音视频应用,而无需下载安装任何插件,Web开发者也无需关注音视频的处理过程,只需编写简单的Javascript程序即可实现。WebRTC库底层是用C/C++实现的,具有良好的跨平台性能,当前已提供对Windows、MAC、iOS和Andriod等多系统的支持,我们只需要调用对应系统的SDK即可完成这些系统上音视频应用的构建。

虽然其名为WebRTC,但是实际上它不光支持Web之间的音视频通讯,还支持Windows、Android以及iOS等移动平台。WebRTC底层是用C/C++开发的,具有良好的跨平台性能。

       WebRTC因为其较好的音视频效果及良好的网络适应性,目前已被广泛的应用到视频会议、实时音视频直播等领域中。在视频会议领域,腾讯会议、华为WeLink、字节飞书、阿里钉钉、科达、ZOOM、小鱼易连均提供了基于WebRTC方案的视频会议。

       大家熟知的音视频专业服务商声网(Agora),更是基于开源WebRTC库,提供了社交直播、教育、游戏电竞、IoT、AR/VR、金融、保险、医疗、企业协作等多个行业的音视频互动解决方案。使用声网服务的企业包括小米、陌陌、斗鱼、哔哩哔哩、新东方、小红书、HTC VIVE 、The Meet Group、Bunch、Yalla等遍布全球的巨头、独角兽及创业企业。

       除了头部公司声网之外,也陆续有多家公司基于开源的WebRTC,开发出了多个音视频应用,提供了多个领域的音视频通信解决方案。

4、在国产化Linux桌面系统中遇到的SDL多线程问题

       在国产化客户端应用程序中我们选择使用SDL库实现音视频的播放,这个播放方案在多个国产化系统都有过验证,是可行的。比如在统信UOS系统、银河麒麟、中标麒麟等国产化操作系统中均进行了详细的测试,音视频均能正常且流畅的播放。

       但在某个项目中确遇到了问题,在客户环境中,其他电脑上客户端软件视频播放都是正常的,唯独在某台电脑上始终有问题,视频无法显示出来,一直显示黑屏。这个有点奇怪,其他国产化电脑上都没问题,唯独这台电脑上有问题。

相对于服务器程序,客户端程序在环境兼容方面有很大的压力和不确定性。服务器硬件设备和操作系统一般都是软件系统提供商提供并固定下来的,不会有很大的变动,服务器程序一般只要在指定的服务器软件环境中充分测试就可以了,们不用考虑对不同软硬件的兼容性问题。
客户端程序则有很大的不同,客户端程序要运行在客户的电脑上,要尽量兼容各式各样电脑的软硬件环境。这里讲的软件,主要是指不同版本的操作系统;硬件则是指电脑上的硬件芯片(比如不通厂商的显卡、USB外接摄像头)及芯片驱动程序。在客户端程序测试过程中,不可能覆盖所有的软硬件环境,所以当产品发布给客户使用后,经常会出现这样或那样的问题。这类兼容性问题,我们遇到过很多次,使用各种办法去处理或规避这些问题。

       为了排查这个问题,我们在调用SDL库接口实现视频播放的代码中添加了大量的打印,重新编译版本,拿到客户出问题的机器上运行。但根据运行日志始终查不出问题。

       在国产化Linux平台上SDL底层使用的是系统OpenGL组件实现视频渲染的,是不是这台问题电脑上的OpenGL组件版本比较低,对多线程的支持不太好。在软件中创建SDL对象的代码和绘制视频图像的代码分别运行在不同的线程中,是不是要挪到同一个线程中呢?

       于是说干就干,动手编写了一个demo程序,将创建SDL对象的代码和绘制视频图像的代码都挪到一个线程中,然后拿到客户的问题电脑上测试,果然这个方法是奏效的,视频如期地显示了出来。于是将修改后的库放到我们的客户端软件中,视频图像果然显示出来了!这个问题折腾的比较久,至此,这个问题总算是解决了!

5、在给WebRTC新增外部音频插件库时遇到的多线程问题

        WebRTC支持以外接插件库的方式添加对音视频额外处理,我们在项目为了解决音频问题,我们额外编写了一个对音频数据做特殊处理的库,以插件的方式接入到WebRTC库中。但添加这个外接插件模块后,遇到了一个很奇怪的问题,Release版本的程序运行是没问题的,但Debug版本程序刚启动就会发生闪退,这个问题是必现的。使用Visual Studio在Debug下发起调试时,程序刚启动就闪退了,Visual Studio看不到有效的函数调用堆栈。遇到这种情况,我们该怎么办呢?我们说过多次了,遇到这种情况,我们直接使用Windbg动态调试就好了。

       因为程序闪退发生在启动时,所以我们选择使用Windbg去启动Debug版本程序。刚启动起来就产生了异常中断,是调用了abort函数引发的中断,查看此时的函数调用堆栈。

以前我们讲过,调用abort函数会让正在调试的调试器Windbg中断下来。因为abort函数内部会raise(产生)一个SIGABRT信号终止异常,如果当前正在调试状态,会让调试器中断下来。abort函数的内部实现源码如下所示:

/***
*void abort() - abort the current program by raising SIGABRT
*
*Purpose:
*   print out an abort message and raise the SIGABRT signal.  If the user
*   hasn't defined an abort handler routine, terminate the program
*   with exit status of 3 without cleaning up.
*
*   Multi-thread version does not raise SIGABRT -- this isn't supported
*   under multi-thread.
*******************************************************************************/
void __cdecl abort (
        void
        )
{
    _PHNDLR sigabrt_act = SIG_DFL;
 
#ifdef _DEBUG
    if (__abort_behavior & _WRITE_ABORT_MSG)
    {
        /* write the abort message */
        _NMSG_WRITE(_RT_ABORT);
    }
#endif  /* _DEBUG */
 
 
    /* Check if the user installed a handler for SIGABRT.
     * We need to read the user handler atomically in the case
     * another thread is aborting while we change the signal
     * handler.
     */
    sigabrt_act = __get_sigabrt();
    if (sigabrt_act != SIG_DFL)
    {
        raise(SIGABRT);
    }
 
    /* If there is no user handler for SIGABRT or if the user
     * handler returns, then exit from the program anyway
     */
    if (__abort_behavior & _CALL_REPORTFAULT)
    {
        _call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);
    }
 
    /* If we don't want to call ReportFault, then we call _exit(3), which is the
     * same as invoking the default handler for SIGABRT
     */
    _exit(3);
}

       根据调用堆栈中显示的模块信息取来了对应的pdb符号库路径,然后查看详细的函数调用堆栈,然后将函数调用堆栈与WebRTC开源库的源码对照起来看,找到了触发abort函数调用的原因。是因为WebRTC开源库内部判断出创建音频设备管理对象的线程与调用RegisterAudioCallback接口设置音频回调的操作所在的线程不在同一个线程,RTC_DCHECK_RUN_ON宏内部会判断是不是同一个线程,然后进入RTC_DCHECK宏,这个RTC_DCHECK宏内部检测到不在同一个线程中,就会调用Fatal_Message接口,然后Fatal_Message接口中又调用了FatalLog接口,FatalLog接口中最终调用了abort函数,将进程强行终止掉了,相关代码截图如下:

这个RTC_DCHECK宏只在Debug下有效,所以Debug下触发了abort函数调用,Release下不起作用,所以Debug下会闪退,而Release不会闪退! 

       由上可知,WebRTC内部要求创建音频设备管理对象的操作与调用RegisterAudioCallback接口设置音频回调的操作必须在同一个线程中,所以问题的解决办法就是,在上层业务模块中将这两个操作放到一个线程中即可。

       使用RegisterAudioCallback方法实现音频处理时,需要遵循一定的线程安全性原则,确保音频数据的采集和处理不会造成多线程竞争条件和死锁等问题。多线程竞争条件解释如下:

多线程竞争条件是指多个线程在同时访问共享资源时,由于访问时序不确定或者操作不完整,导致结果出现错误或者不确定的情况。竞争条件是多线程编程中常见的一种问题,如果处理不当,可能会导致程序出现各种问题,如崩溃、死锁、数据损坏等。
竞争条件的解决方案包括:
锁机制:使用互斥锁(Mutex)、读写锁(ReadWriteLock)等同步机制,保证每个线程对共享资源的访问是互斥的,防止出现竞争条件。
原子操作:使用原子操作(Atomic Operation)可以保证对共享资源的访问是原子性的,即不会被中断,保证了访问的完整性和一致性。
条件变量:使用条件变量(Condition Variable)可以实现线程之间的通信和协调,避免出现死锁和饥饿等问题。
信号量:使用信号量(Semaphore)可以限制并发线程的数量,防止过多的线程同时访问共享资源。
总之,多线程编程中需要注意竞争条件问题,保证线程安全和程序正确性。合理地使用锁机制、原子操作、条件变量和信号量等同步机制,可以有效地避免竞争条件问题的出现。

6、最后

       上面两个使用开源库时遇到的线程约束问题,是我们在实际项目中遇到的,之前没碰到过这类线程约束问题。今天在这里做个详细的总结,给大家提供一个借鉴或参考。

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

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

相关文章

【云原生】创建容器的方法

1)基于现有镜像的创建 先使用现有镜像创建容器 docker run 再进入容器进行内容更新 docker exec 最后提交成新的镜像 docker commit 2)基于模板创建 可以从本地容器导出模板文件 docker export 或者从网上下载现成的模板文件 http://openvz…

C++继承相关内容(二)

目录 一.拷贝构造函数 第一种情况:基类没有拷贝构造函数,派生类也没有拷贝构造函数 结果: 原因: 第二种情况:基类没有拷贝构造函数,派生类有拷贝构造函数 结果: 原因: 第三种情况…

【文生图系列】 Stable Diffusion v2复现教程

文章目录 xformersbug 记录 txt2imgdiffusers参考 基础环境承接Stable Diffusion v1, 详情请见我的博文【文生图系列】 Stable Diffusion v1复现教程。然后更新pytorch和torchvision的版本,因为要使用GPU和xformers,需要下载gpu版本的pytorch。再下载ope…

学习笔记之微服务(一)

一、了解微服务 1、服务架构演变 **单体架构:**所有业务功能都集中在一个项目中开发,打成一个包部署。 优点:架构简单、部署成本低 缺点:耦合度高 分布式架构:根据业务拆分系统功能,每个业务模块独立项…

微信小程序map 之个性化地图(日出日落主题)

微信小程序map 之个性化地图(日出日落主题) 个性化地图之根据日出日落时间动态变换地图主题个性化前的准备进入腾讯地址服务官网小程序开发html 代码. layer-style 编号为样式名称js代码. 注意的是,layer-style只能定义一次,所以值…

Yarn【常用命令】

1、yarn application 查看Application运行情况 1.1、列出所有Application yarn application -list 可以通过Web UI端来查看: 1.2、根据Application状态过滤: yarn application -list -appStates (所有状态: ALL 、 NEW 、 NEW…

Spring Cloud Alibaba 同时兼容dubbo与openfeign

一、前言 dubbo与springcloud都可以单独作为微服务治理框架在生产中进行使用,但使用过springcloud的同学大概了解到,springcloud生态的相关组件这些年已经逐步停更,这就导致在服务架构演进过程中的迭代断层,以至于一些新的技术组…

聚合/组合

谨慎使用继承的方式来进行扩展,优先使用聚合/组合的方式来实现。 Father类里有两个方法A和方法B,并且A调用了B。子类Son重写了方法B,这时候如果子类调用继承来的方法A,那么方法A调用的就不再是Father.B(),而是子类中的…

使用kali里的dnschef进行DNS欺骗

1. 前言 DNSChef是针对渗透测试人员和恶意软件分析师的高度可配置的DNS代理。它能够精细配置哪些DNS回复以修改或简单地代理真实响应。为了利用该工具,您必须手动配置DNS服务器以指向DNSChef。 2. 执行参数 选项参数: -h,--help显示帮助信息并退出--f…

C语言:计算 1! + 2! + 3! + ... + n!

题目: 从键盘输入一个值n,计算 1的阶乘 至 n的阶乘 的和, 如:输入10,计算 1的阶乘 至 n的阶乘 的和 --> 计算:1! 2! 3! ... 10! 思路一: 效率比较低,会重复计算之前计算过的…

Stable Diffusion 从听说到超神日记

1.安装模型(B站搜秋叶) 2.下载模型,安装好的界面有模型下载 有了上面模型已经可以根据模型风格去生成一些照片了 3.使用Additional Networks/lora模型 模型大多都是继承了很多网友自行训练的图库模型,各类风格都有! …

分享一个卡通人物

这几天较忙&#xff0c;像它一样&#xff1a; 代码在这里&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>卡通打字</title><script src"https://s3-us-west-2.amazonaws…

《MySQL(五):基础篇- 多表查询》

文章目录 5. 多表查询5.1 多表关系5.1.1 一对多5.1.2 多对多5.1.3 一对一 5.2 多表查询概述5.2.1 数据准备5.2.2 概述5.2.3 分类 5.3 内连接5.4 外连接5.5 自连接5.5.1 自连接查询5.5.2 联合查询5.6.2 标量子查询5.6.3 列子查询5.6.5 表子查询 5.7 多表查询案例 5. 多表查询 我…

【Sql】根据字段分组排序,取其第一条数据

【Sql】根据字段分组排序&#xff0c;取组内第一条数据 【一】问题描述【二】解决方案&#xff08;oracle和mysql都可用&#xff09;【三】总结 【一】问题描述 &#xff08;1&#xff09;问题描述 有时候我们需要对数据进行去重处理&#xff0c;例如查询结果里的文件名有重复…

常用模拟低通滤波器的设计——契比雪夫I型滤波器

目录 常用模拟低通滤波器的设计——契比雪夫I型滤波器 1、cheb1ap 函数 2、cheb1ord 函数 3、cheby1函数 4、实例演示 常用模拟低通滤波器的设计——契比雪夫I型滤波器 巴特沃斯滤波器的频率特性曲线在通带和阻带内&#xff0c;幅度特性是单调下降的&#xff0c;如果阶次一…

RISC-V体系结构的U-Boot引导过程

RISC-V体系结构的U-Boot引导过程 flyfish BootLoader Boot是Bootstrap&#xff08;鞋带&#xff09;的缩写&#xff0c;它来自于一句谚语&#xff1a;“Pull oneself up by one’s own bootstraps”&#xff0c;直译的意思是“拽着鞋带把自己拉起来” 干这件事&#xff0c;得…

文心一言 VS 讯飞星火 VS chatgpt (33)-- 算法导论5.2 5题

五、设A[1…n]是由n个不同数构成的数列。如果i<j且A[i]>A[j]&#xff0c;则称(i&#xff0c;j)对为A的一个逆序对(inversion)。(参看思考题2-4 中更多关于逆序对的例子。)假设A的元素构成(1&#xff0c;2&#xff0c;… &#xff0c;n)上的一个均匀随机排列。请用指示器随…

Win11配置Anaconda-Cuda-Pytorch-Tenserflow环境

之前搞了一个月左右时间用WSL2跑模型&#xff0c;后来发现还是不太习惯&#xff08;其实用不明白&#xff09;&#xff0c;就转成Windows了。这次也是记录一下自己配环境的过程&#xff0c;以免下次还需要 安装Anaconda 下载方式 Anaconda官网清华镜像下载 tip&#xff1a;…

OpenHarmony源码解析(11): hiview维测平台

作者: 鸿湖万联(武汉) 许文龙 1、概述 Hiview是一个跨平台的终端设备维测服务集,由插件管理平台和基于平台上运行的服务插件来构成整套系统。Hiview维测服务是由HiSysEvent事件驱动的,其核心为分布在系统各处的HiSysEvent桩点,格式化的事件会通过HiSysEvent打点API上报至H…

Tomcat【尚硅谷】

一、Tomcat基础 1、常见的web服务器 概念 服务器&#xff1a;安装了服务器软件的计算机服务器软件&#xff1a;接收用户的请求&#xff0c;处理请求&#xff0c;做出响应web服务器软件&#xff1a;在web服务器软件中&#xff0c;可以部署web项目&#xff0c;让浏览器来访问这…