WebRTC引用计数和线程

news2025/1/16 5:12:01

1. 什么是引用计数:

引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存、或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。
使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。

2. 线程:

WebRTC中实现了跨平台(Windows、MacOS、Linux、iOS、Android)的线程类: rtc::Thread,
代码位于 rtc_base/ 目录下的 thread.h 和 thread.cc 中。

WebRTC线程模型:
在这里插入图片描述

2.1 WebRTC线程的创建和使用:

对WebRTC的线程模型可以从创建和运行两个不同的角度来理解。

一、从创建的角度,WebRTC的线程被划分未两层:

(1)第一层由三个线程构成:网络线程、工作线程、信令线程

这三个线程可以在WebRTC库外由用户自己创建后传给WebRTC管理,也可以由WebRTC在构造PeerConnectionFactory对象时自己创建。一般情况下使用后一种方法,即让WebRTC自己创建三个线程。实际上,当WebRTC自己创建线程时,其实只创建了两个线程—网络线程 和 工作线程,而信令线程则由主线程来代替。

实现代码:

// src/pc/peer_connection_factory.cc
PeerConnectionFactory::PeerConnectionFactory() {
    if (!network_thread_) {
    	owned_network_thread_ = rtc::Thread::CreateWithSocketServer();
    	owned_network_thread_->SetName("pc_network_thread", nullptr);
    	owned_network_thread_->Start();
    	network_thread_ = owned_network_thread_.get();
    }


    if (!worker_thread_) {
    	wned_worker_thread_ = rtc::Thread::Create();
    	owned_worker_thread_->SetName("pc_worker_thread", nullptr);
    	owned_worker_thread_->Start();
    	worker_thread_ = owned_worker_thread_.get();
    }

	// 信令线程指向当前线程,即主线程:
    if (!signaling_thread_) {
		signaling_thread_ = rtc::Thread::Current();
	    if (!signaling_thread_) {
	      // If this thread isn't already wrapped by an rtc::Thread, create a
	      // wrapper and own it in this class.
	      signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread();
	      wraps_current_thread_ = true;
	    }
}

(2)第二层子线程,由第一层的三个主线程创建:
在 网络线程、工作线程、信令线程 创建好之后,它们会根据业务需要创建子线程,从而形成第二层线程。
其中,工作线程会创建 视频编码线程、视频解码线程、音频编码线程、Pacer线程等;
信令线程会创建视频采集线程、视频渲染线程、音频采集线程、音频渲染线程等。

WebRTC线程模型中的第一层线程是必须存在的,也就是说只要使用WebRTC库,三个线程就必然会创建;第二层线程则不一定存在,比如使用WebRTC时如果没有开启音视频业务,就不会创建与音视频相关的线程。

二、从运行的角度看,WebRTC的线程模型分为三层:
第一层还是网络线程、工作线程、信令线程;
第二层是音视频编解码层;
第三层是硬件访问层,用于进行音视频的采集和渲染。

2.2 WebRTC线程切换:

线程切换原理:
在这里插入图片描述

数据会在WebRTC的线程之间不断地切换(传递),为了实现这种数据的传递,每个线程对象都是由 线程本身 和 队列 组成。
队列中可以存放任何类型的数据,既可以是音视频这种二进制数据,也可以是一个任务对象。
对于接收线程来说,它通过一个while循环不停地从自己的队列中取出数据进行处理。
例如图中,当线程一将处理好的数据交换到线程二进行处理时,它只需将自己输出的数据/任务 插入到线程二的队列中即可。

WebRTC提供了两种线程间切换的方法: Post() 和 Send()。

Post():异步的,构造一个Message对象,将其插入到接收方线程的队列中即可;
Send():同步的,将构造的Message插入到线程队列之后,需要等待接收方完成任务之后,发送方才能继续自己的处理逻辑。

在Post()和Send()方法的基础上,WebRTC还提供了两种变型的方法用于线程切换:PostTask()、Invoke()。

Post()方法的定义及使用:

定义:

// rtc_base/thread.h

// Post:
virtual void Post(const Location& posted_from,		// 记录发生线程切换的位置,该参数对于排查问题有帮忙,可以快速定位到问题所在的线程,除此之外它不参与任何逻辑处理
 					MessageHandler* phandler,		// phandler是一个包含OnMessage()方法的类对象,当线程发生切换时,目标线程会调用该参数的OnMessage()方法做进一步的逻辑处理(可以简单理解为传给目标线程的任务回调函数)
 					uint32_t id = 0,				// 表示消息id,当源线程处理的数据包含多种类型(例如音频、视频)时,目标线程可以通过该参数对数据进行区分
 					MessageData* pdata = nullptr,	// 数据指针,指向目标线程需要处理的数据地址
 					bool time_sensitive = false);	// 此参数以被淘汰不使用,一直为flase

使用Post()方法:

...
signaling_thread()->Post(RTC_FROM_HERE, 			// 宏,用于获取当前的位置信息,实际上就是在其内部生成一个带有函数名和行号的Location对象
							this, 					// this指针,即指向调用Post方法的对象,注意该对象必须是继承自 MessageHandler类的,且实现了OnMessage()方法
							MSG_FIRSTPACKETRECEIVED); // 消息ID,指明是什么信息

Post()方法的实现:
Post()方法的实现非常简单,就是将输入的参数填入到Message对象中,然后将其插入到目标线程的message_队列中。

//

void Thread::Post(...) {
    CritScop cs(&crit_);
    Message msg;
    msg.posted_from = posted_from;
    msg.phandler = phandler;
    msg.message_id = id;
    msg.pdata = pdata;
    messages_.push_back(msg);
    ...
}

消息接收方(目标线程)的处理:
当目标线程启动时,它会执行PreRun()方法,PreRun()方法又会调用Run()方法,而Run()方法最终调用ProcessMessage()方法实现消息的分发与处理,调用过程如下:

启动线程(Thread)->PreRun()->Run()->ProcessMessage()

PorcessMessage()方法的实现:

bool Thread::ProcessMessage(int cmsLoop) {
	...
	while (true) {
		Message msg;
		if (!Get(&msg, cmsNext))	// 在while死循环中不断调用Get()方法,从线程的message_队列中获取消息
			return !IsQuiting();
		
		Dispatch(&msg);	// 处理从队列中获取到的消息
		...
	}
	...
}

void Thread::Dispatch(Message* pmsg) {
	...
	pmsg->phandler->OnMessage(pmsg);	// 调用Message对象中phandler对象的OnMessage()方法,最终完成消息的处理
	...
}

PostTask():

PostTask()方法是从Post()方法衍生出来的,其整体逻辑与Post()方法是一致的。

PostTask()方法的实现:
PostTask()方法中也是调用了Post()方法来实现的,它将Post的第三个参数写死(id=0),只允许改变第四个输入参数。

void Thread::PostTask(... task) {
	Post(RTC_FROM_HERE,
			&queued_task_handler_,
			0,							// id
			new ...(std::move(task)));	
}

OnMessage()方法的实现:

void QueuedTaskHandler::OnMessage(Message* msg) {
	...
	auto* data = static_cast<...>(msg->pdata);
	std::unique_ptr<..>task = std::move(data->data());
	...
	if (!task->Run())
	...
}

线程:

  1. 《WebRTC源码分析-线程基础概述》
    https://blog.csdn.net/ice_ly000/article/details/103178702
  2. 《WebRTC源码分析-线程基础之线程管理》
    https://blog.csdn.net/ice_ly000/article/details/103178691
  3. 《WebRTC源码分析-线程基础之线程基本功能》
    https://blog.csdn.net/ice_ly000/article/details/103178692
  4. 《WebRTC源码分析-线程基础之MessageQueueManager》
    https://blog.csdn.net/ice_ly000/article/details/103178701
  5. 《WebRTC源码分析-线程基础之Message && MessageData && MessageHandler》
    https://blog.csdn.net/ice_ly000/article/details/103178700
  6. 《WebRTC源码分析-线程基础之MessageQueue》
    https://blog.csdn.net/ice_ly000/article/details/103178697
  7. 《WebRTC源码分析-线程基础之消息循环,消息投递》
    https://blog.csdn.net/ice_ly000/article/details/103178695
  8. 《WebRTC源码分析-线程基础之跨线程同步MethodCall》
    https://blog.csdn.net/ice_ly000/article/details/103189878
  9. 《WebRTC源码分析-线程安全之Proxy,防止线程乱入》
    https://blog.csdn.net/ice_ly000/article/details/103191668

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

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

相关文章

纯HTML+CSS网页设计期末作业(海贼王主题网站——图片可换,主题可换)

纯HTMLCSS网页设计期末作业&#xff08;海贼王主题网站——图片可换&#xff0c;主题可换&#xff09; 博主&#xff1a;命运之光 专栏&#xff1a;web网页制作大作业 网页最底下有视频可以观看网页效果&#xff0c;喜欢的可以在博主主页资源里免费下载(●’◡’●)让我们一起加…

[数据库系统] 一、添加常用约束(educoder)

1.任务&#xff1a;给表添加常用的约束。 2.相关知识 在数据库的使用过程中我们经常要限制属性的取值&#xff0c;比如有些属性不能为空&#xff0c;就需要添加非空约束&#xff0c;本关要求完成常用约束的添加和定义。 目录 (1)唯一约束 (2)添加非空约束 (3)使用默认约束…

大数据|Hive和数据仓库

前文回顾&#xff1a;HBase基本工作原理 目录 &#x1f4da;数据仓库和OLAP &#x1f407;数据仓库 &#x1f955;面向主题 &#x1f955;集成的 &#x1f955;时变的 &#x1f955;非易失的 &#x1f407;OLTP&#xff08;联机事务处理&#xff09;vs OLAP&#xff08;…

织梦城市分站怎么安装织梦二级域名站群织梦制作企业分站教程

1、安装说明 一、下载织梦多城市二级域名源码; 二、上传源码到服务器;(必须支持泛解析) 三、在浏览器输入http://你的域名/install进入安装页面; 可以参考http://www.hlzcb.com/zhimengxueyuan/zhimenganzhuangshiyong/25830.html 四、输入数据库用户名密码和数据库…

公司股权转让,变更股东要了解哪些?

什么是公司股权&#xff1f; 股权即股票持有者所具有的与其拥有的股票比例相应的权益及承担一定责任的权力。股权转让是一种物权变动行为&#xff0c;股权转让后&#xff0c;股东基于股东地位而对公司所发生的权利义务关系全部同时移转于受让人&#xff0c;受让人因此成为公司…

SpringBoot的yml多环境配置3种方法

目录 方式一&#xff1a;多个yml文件 步骤一、创建多个配置文件 步骤二、applicaiton.yml中指定配置 方式二&#xff1a; 单个yml文件 方式三&#xff1a;在pom.xml中指定环境配置 步骤一、创建多个配置文件 步骤二、在application.yml中添加多环境配置属性 步骤三、在po…

创客匠人直播怎么样?

快速发展的互联网时代&#xff0c;直播成为各个行业的引流利器。如今&#xff0c;众多教培机构转型线上&#xff0c;直播无疑是最好的线上教学方式&#xff0c;不仅可以让老师和学员异地教学还能最好的还原线下教学场景&#xff0c;利用科技提高学习质量。 做好一场直播不仅仅…

JColorChooser和JFileChooser

Swing提供了JColorChooser和JFileChooser这两种对话框&#xff0c;可以很方便的完成演示的选择和本地文件的选择。 1.JColorChooser JColorChooser用于创建颜色选择器对话框&#xff0c;该类的用法非常简单&#xff0c;只需要调用它的静态方法就可以快速生成一个颜色选择对话框…

蓝牙耳机什么牌子好?500内好用的蓝牙耳机推荐

随着蓝牙耳机的受欢迎程度越来越高&#xff0c;近几年来&#xff0c;无蓝牙耳机市场呈爆发式增长&#xff0c;蓝牙耳机品牌也越来越多。那么蓝牙耳机什么牌子好&#xff1f;接下来&#xff0c;我来给大家推荐几款500内好用的蓝牙耳机&#xff0c;一起来看看吧。 一、南卡小音舱…

GDD471A001 PLC / DCS维护日志

​ GDD471A001 PLC / DCS维护日志 PLC维护日志 PLC/DCS 维护日志将帮助您跟踪过去的故障、解决方案、零件更换。如果以后再次出现同样的问题&#xff0c;跟踪日志将帮助您立即解决。 您的控制系统的可靠性可以通过参考维护日志来确定。 使用 PLC/DCS 维护日志可以识别频繁出…

React的生命周期及Redux状态管理器等

生命周期 一个应用或页面从创建到消亡过程中某一时刻自动调用的回调函数称为生命周期钩子函数 挂载 constructor &#xff1a;来初始化函数内部 state&#xff0c;为 事件处理函数 绑定实例render&#xff1a;渲染 DOMcomponentDidMount&#xff1a;组件挂载、DOM 渲染完后&…

V8 JavaScript引擎

简介 V8 (v8.dev)是 Google 的开源高性能 JavaScript 和 WebAssembly 引擎&#xff0c;用 C 编写。它用于 Chrome 和 Node.js 等。它实现了 ECMAScript 和 WebAssembly&#xff0c;并运行在 Windows 7 或更高版本、macOS 10.12 以及使用 x64、IA-32、ARM 或 MIPS 处理器的 Lin…

FFmpeg HEVC 解码 YUV

1. 概要与流程图 1.1 FFmpeg 支持 h264,hevc 等解码,由于分离视频文件为 hevc 格式,为了方便起见,当前解码的格式为 hevc,代码支持各种视频格式解码,需要修改参数和适配 1.2 HEVC 解码 YUV 流程图如下: 2. 封装读写文件操作 2.1 读写头文件,FileTool.h #import <Fou…

MAC常用操作

1. 添加环境变量 vi ~/.bash_profile export PATHselfdefine_path:$PATH source ~/.bash_profile适用于安装Application之后将该Application的Contents/bin下的可执行程序添加到环境变量&#xff0c;使得在终端能够启用。 例如使用cmake-3.25.0-macos-universal.dmg安装好cmak…

基于微信小程序网上书城系统

开发工具&#xff1a;IDEA、微信小程序 服务器&#xff1a;Tomcat8.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 前端技术&#xff1a;vue、uniapp 服务端技术&#xff1a;springspringmvcmybatis(ssm框架) 本系统分微信小程序和管理后…

双向链表(数据结构)(C语言)

目录 概念 带头双向循环链表的实现 前情提示 双向链表的结构体定义 双向链表的初始化 关于无头单向非循环链表无需初始化函数&#xff0c;顺序表、带头双向循环链表需要的思考 双向链表在pos位置之前插入x 双向链表的打印 双链表删除pos位置的结点 双向链表的尾插 关…

Windows命令提示行使用指南一

命令提示行使用指南 前言一、起源和发展二、和DOS的关系三、常用命令 前言 cmd 是 Windows 操作系统中的命令行界面&#xff08;CLI&#xff09;&#xff0c;也称为命令提示符&#xff08;CMD&#xff09;或批处理文件。它是 Windows 命令行界面的主要组成部分&#xff0c;用于…

《Linux 内核设计与实现》07. 中断和中断处理

文章目录 注册中断处理程序释放中断处理程序编写中断处理程序共享的中断处理程序中断例程实例 中断上下文/proc/interrupts中断控制禁止和激活中断禁用指定中断线中断系统的状态 注册中断处理程序 // 分配一条给定的 irq 中断线 request_irq(unsigned int irq, irq_handler_t …

『python爬虫』13. 视频地址防盗链实战案例(保姆级图文)

目录 1. 寻找视频真实url地址&#xff08;视频地址被加密了&#xff09;2. 防盗链中的来源判断完整代码总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 1. 寻找视频真实url地址&#xff08;视频地址被加密了&am…

【刷题笔记】不要二+把字符串转换为整数

一、不要二 题目&#xff1a; 牛客网链接&#xff1a;不要二_牛客题霸_牛客网 描述 二货小易有一个W*H的网格盒子&#xff0c;网格的行编号为0~W-1&#xff0c;网格的列编号为0~H-1。每个格子至多可以放一块蛋糕&#xff0c;任意两块蛋糕的欧几里得距离不能等于2。 对…