【MediaSoup---源码篇】(二)Router

2023-09-22 12:01:04
概述

Router:路由对象,类似于房间的功能,保存了流之间的订阅关系,它接收 Producer 的数据并转发给订阅该 Producer 的 Consumer

Router继承了以下类:

RTC::Transport::Listener,RTC::RtpObserver::Listener,Channel::ChannelSocket::RequestHandler

RTC::Transport,它用于在mediasoup与客户端之间传输实时音视频数据

RTC::RtpObserver,是一个用于观察和处理实时传输协议(RTP)数据的接口

Channel::ChannelSocket,是用于响应与上层之间的传输

{   //router和房间是一对一的关系,一个router就表示一个房间。当router接收到房间内成员的RTP数据后,会转发给所有房间内的其它成员,或者其它worker。
	class Router : public RTC::Transport::Listener,
	               public RTC::RtpObserver::Listener,
	               public Channel::ChannelSocket::RequestHandler
创建Router房间的流程

 在Worker层,首先上层会传入一个创建Router的信令,先到达Worker

inline void Worker::HandleRequest(Channel::ChannelRequest* request)

 通过类型判断进入到创建房间中,其中在Worker中保存了roomid与Router实例的映射关系

        //创建房间
		case Channel::ChannelRequest::MethodId::WORKER_CREATE_ROUTER:
		{
			std::string routerId;//保存上层传递的roomid

			try
			{   
				//根据上层信令传入的roomid来创建底层的房间概念
				SetNewRouterIdFromData(request->data, routerId);
			}
			catch (const MediaSoupError& error)
			{
				MS_THROW_ERROR("%s [method:%s]", error.what(), request->method.c_str());
			}
            //创建好了Router与Router内处理各种消息的事宜
			auto* router = new RTC::Router(this->shared, routerId, this);
            //把routerId和router实例进行绑定
			this->mapRouters[routerId] = router;

			MS_DEBUG_DEV("Router created [routerId:%s]", routerId.c_str());

			request->Accept();

			break;
		}
Router构造函数
	/* Instance methods. */
    // 实例化`Router` 类并注册其消息处理程序的构造函数。
	Router::Router(RTC::Shared* shared, const std::string& id, Listener* listener)
	  : id(id), shared(shared), listener(listener)
	{
		MS_TRACE();
        /*
		这段代码是C++中的函数调用,用于将一个请求处理程序注册到一个通道消息注册器中。具体来说,这个函数调用的参数包括:
        -`this->id`:表示当前对象的id或标识符。
        -`channelRequestHandler`:指向请求处理函数的指针。
        -`payloadChannelRequestHandler`:指向负载请求处理函数的指针,此处为nullptr,表示没有负载请求处理函数。
        -`payloadChannelNotificationHandler`:指向负载通知处理函数的指针,此处为nullptr,表示没有负载通知处理函数。
        通过注册请求处理程序,可以在接收到相应的通道消息时,调用相应的处理函数对消息进行处理。这个函数调用是一种常见的使用C++进行事件驱动编程的方式。
		*/
		// NOTE: This may throw.
		this->shared->channelMessageRegistrator->RegisterHandler(
		  this->id,
		  /*channelRequestHandler*/ this,
		  /*payloadChannelRequestHandler*/ nullptr,
		  /*payloadChannelNotificationHandler*/ nullptr);
	}

对以上代码深度解析,首先在Router中持有了RTC::Shared 和  Listener。

RTC::Shared实际为Worker中的shared,Listener为Worker.

在构造函数中有一个重要的地方,我们来分析一下this->shared->channelMessageRegistrator->RegisterHandler,这句调用的意义在于,将当前的Router注册为响应通道消息的处理函数,如果通道发生了响应消息,就会自动转到该Router中进行相应处理。其中保存的方法为roomid与房间实例进行映射。

/将当前路由器实例注册为消息处理程序。
//`RegisterHandler()` 方法为 mediasoup 应用程序提供通用消息通信区域,并可将不同的消息分发给不同的路由器实例进行处理。
void ChannelMessageRegistrator::RegisterHandler(
  const std::string& id,//当前路由器实例的 ID,用于标识路由器实例
  Channel::ChannelSocket::RequestHandler* channelRequestHandler,//指向处理特定类型的通用请求的函数的指针
  PayloadChannel::PayloadChannelSocket::RequestHandler* payloadChannelRequestHandler,//通用请求和通知处理程序,这些处理程序用于处理特定类型的请求和通知消息
  PayloadChannel::PayloadChannelSocket::NotificationHandler* payloadChannelNotificationHandler)
{
	MS_TRACE();
    //channelRequestHandler理论上为创建Router时候的实例
	if (channelRequestHandler != nullptr)
	{
		if (this->mapChannelRequestHandlers.find(id) != this->mapChannelRequestHandlers.end())
		{
			MS_THROW_ERROR("Channel request handler with ID %s already exists", id.c_str());
		}
        //把id作为key,处理特定类型的通用请求的函数的指针作为value保存
		this->mapChannelRequestHandlers[id] = channelRequestHandler;
	}
    //理论说用transport的时候会进行绑定
	if (payloadChannelRequestHandler != nullptr)
	{   //如果存在旧的,就先删除
		if (this->mapPayloadChannelRequestHandlers.find(id) != this->mapPayloadChannelRequestHandlers.end())
		{
			if (channelRequestHandler != nullptr)
			{
				this->mapChannelRequestHandlers.erase(id);
			}

			MS_THROW_ERROR("PayloadChannel request handler with ID %s already exists", id.c_str());
		}
        //重新为本次分配
		this->mapPayloadChannelRequestHandlers[id] = payloadChannelRequestHandler;
	}

	if (payloadChannelNotificationHandler != nullptr)
	{
		if (
		  this->mapPayloadChannelNotificationHandlers.find(id) !=
		  this->mapPayloadChannelNotificationHandlers.end())
		{
			if (channelRequestHandler != nullptr)
			{
				this->mapChannelRequestHandlers.erase(id);
			}

			if (payloadChannelRequestHandler != nullptr)
			{
				this->mapPayloadChannelRequestHandlers.erase(id);
			}

			MS_THROW_ERROR("PayloadChannel notification handler with ID %s already exists", id.c_str());
		}

		this->mapPayloadChannelNotificationHandlers[id] = payloadChannelNotificationHandler;
	}
}

当发生通道消息时候会先通过ID查找对应Router实例

//通过路由器ID查找相应的处理函数
Channel::ChannelSocket::RequestHandler* ChannelMessageRegistrator::GetChannelRequestHandler(
  const std::string& id)
{
	MS_TRACE();

	auto it = this->mapChannelRequestHandlers.find(id);

	if (it != this->mapChannelRequestHandlers.end())
	{
		return it->second;
	}
	else
	{
		return nullptr;
	}
}

在Worker中触发上面所说的

		default:
		{
			try
			{   //事件驱动机制通过id来查找相对应的处理模块
				auto* handler =
				  this->shared->channelMessageRegistrator->GetChannelRequestHandler(request->handlerId);

				if (handler == nullptr)
				{
					MS_THROW_ERROR("Channel request handler with ID %s not found", request->handlerId.c_str());
				}
                //一般是Router::HandleRequest
				handler->HandleRequest(request);
			}
			catch (const MediaSoupTypeError& error)
			{
				MS_THROW_TYPE_ERROR("%s [method:%s]", error.what(), request->method.c_str());
			}
			catch (const MediaSoupError& error)
			{
				MS_THROW_ERROR("%s [method:%s]", error.what(), request->method.c_str());
			}

			break;
		}

经过上面的周转,又回来的Router中的处理

    //房间内部处理指令
	void Router::HandleRequest(Channel::ChannelRequest* request)

更多推荐

【中秋国庆】旅行公众号文章排版素材大全

中秋国庆节长假即将来临,你是否已经做好了旅行计划?在这个举国同庆的时刻,何不走出家门,去感受大自然的壮美、领略历史的厚重以及品尝地道的美食呢?随着假期的临近,各大公众号纷纷推出了相关文章,为节日的氛围增色添彩。今天小编将为宝子们分享一些旅行公众号文章排版素材,帮助你在这个假期里,呈现出一篇富有诗意和浪漫的旅行日记。在排

NSDT孪生场景编辑器系统介绍

一、产品背景数字孪生的建设流程涉及建模、美术、程序、仿真等多种人才的协同作业,人力要求高,实施成本高,建设周期长。如何让小型团队甚至一个人就可以完成数字孪生的开发,是数字孪生工具链要解决的重要问题。考虑到数字孪生复杂的生产流程,一个面向小型团队的数字孪生开发工具应该考虑以下问题:NSDT编辑器的出现很好解决了以上问题,

day29IO流(其他流)

1.缓冲流昨天学习了基本的一些流,作为IO流的入门,今天我们要见识一些更强大的流。比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等。这些功能更为强大的流,都是在基本的流对象基础之上创建而来的,就像穿上铠甲的武士一样,相当于是对基本流对象的一种增强。1.1概述缓冲流,也叫高效流,是对4个基

你对java的原子性了解多少?

你对java的原子性了解多少?java的原子性你对java的原子性了解多少?java里的原子性是什么java实现原子性的原理是什么java如何实现原子性java里的原子性是什么在Java中,原子性是指一个操作是不可被中断的整体操作。原子性确保一个操作在多线程环境下执行时,不会被其他线程干扰,要么完全执行成功,要么完全不

Linux用户管理指南:创建、删除、权限、最佳实践,全面掌握用户管理技巧

文章目录Linux用户管理指南1.简介1.1什么是Linux用户管理?1.2为什么Linux用户管理重要?2.用户账户创建和删除2.1创建用户账户2.2删除用户账户2.3设置用户账户的属性3.用户登录和注销3.1远程登录3.2本地登录3.3强制用户注销4.用户密码管理4.1密码策略4.2修改用户密码4.3重置用户密码5

Linux 本地 Docker Registry本地镜像仓库远程连接【内网穿透】

Linux本地DockerRegistry本地镜像仓库远程连接文章目录Linux本地DockerRegistry本地镜像仓库远程连接1.部署DockerRegistry2.本地测试推送镜像3.Linux安装cpolar4.配置DockerRegistry公网访问地址5.公网远程推送DockerRegistry6.固定D

分布式/微服务---第六篇

系列文章目录文章目录系列文章目录一、简述zk的命名服务、配置管理、集群管理二、讲下Zookeeperwatch机制一、简述zk的命名服务、配置管理、集群管理命名服务:通过指定的名字来获取资源或者服务地址。Zookeeper可以创建一个全局唯一的路径,这个路径就可以作为一个名字。被命名的实体可以是集群中的机器,服务的地址

阿里云无影云电脑和传统PC有什么区别?

阿里云无影云电脑和传统电脑PC有什么区别?区别大了,无影云电脑是云端的桌面服务,传统PC是本地的硬件计算机,无影云电脑的数据是保存在云端,本地传统PC的数据是保存在本地客户端,阿里云百科分享阿里云无影云电脑和传统PC电脑的详细区别对比:目录无影云电脑和传统电脑区别对比阿里云无影云电脑无影云电脑和传统电脑区别对比阿里云无

创建型-单例模式-实现和优缺点

一、实现方式:单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来访问该实例。以下是几种实现单例模式的常见方式:懒汉式(LazyInitialization):这种方式在第一次使用时才创建单例实例,而不是在应用程序启动时就创建。这可以节省资源。publicclassSingleton{privat

数据清洗:数据挖掘的前期准备工作

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️🐴作者:秋无之地🐴简介:CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作,主要擅长领域有:爬虫、后端、大数据开发、数据分析等。🐴欢迎小伙伴们点赞👍🏻、收藏⭐️、留言💬、关注🤝,关注必回关上一篇文章已经跟大家介绍过

在docker中安装MQTT教程

网上的好多关于在docker中安装MQTT教程都是错误的不完整的。这篇博客是完整的,实践过的,踩过了很多的坑得来的,欢迎大家享用!1、首先在docker中拉取镜像dockerpulleclipse-mosquitto2、创建配置文件目录mkdir-p/docker/mosquitto/configmkdir-p/doc

热文推荐