我是靠谱客的博主 畅快野狼,最近开发中收集的这篇文章主要介绍【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析

【1】MediaSoup 的整体架构

Worker

  • A worker represents a mediasoup C++ subprocess that runs in a single CPU core and handles Router instances 
  • Worker 表示 MediaSoup C++ 的子进程,该子进程运行在一个单独的 CPU 核心中,用于处理 Router 实例

Router

  • A router enables injection, selection and forwarding of media streams through Transport instances created on it
  • Router 通过基于其创建的 Transport 实例实现媒体流的注入、选择、转发

Transport

  • A transport connects an endpoint with a mediasoup router and enables transmission of media in both directions by means of Producer, Consumer, DataProducer and DataConsumer instances created on it
  • Transport 连接终端与 MediaSoup Router 并且通过基于其创建的 Producer, Consumer 实例实现媒体数据的双向传输

Producer

  • A producer represents an audio or video source being injected into a mediasoup router. It's created on top of a transport that defines how the media packets are carried
  • Producer 表示注入到 MediaSoup Router 中的音频 / 视频源,Producer 基于 Transport 创建,Transport 定义了媒体数据包的传输方式

Consumer

  • A consumer represents an audio or video source being forwarded from a mediasoup router to an endpoint. It's created on top of a transport that defines how the media packets are carried
  • Consumer 表示从 MediaSoup Router 中转发出来的音频 / 视频源,Consumer 基于 Transport 创建,Transport 定义了媒体数据包的传输方式

【2】Worker 的创建过程分析

  • MediaSoup-Demo
模块 : mediasoup-demoserver
文件 : serverserver.js
方法 : 
async function runMediasoupWorkers()
功能 : 
创建 worker 实例,实例数量等同于 CPU 核心数量,设置 worker 实例监听 died 事件,周期性打印 worker 资源使用情况
  • MediaSoup NodeJS 部分
模块 : mediasoup
文件 : srcindex.ts
方法 : 
export async function createWorker(
	{
		logLevel = 'error',
		logTags,
		rtcMinPort = 10000,
		rtcMaxPort = 59999,
		dtlsCertificateFile,
		dtlsPrivateKeyFile,
		appData = {}
	}: WorkerSettings = {}
): Promise<Worker>
功能 :
创建 worker 实例
监听 '@success' 事件 -- 创建成功
监听 '@failure' 事件 -- 创建失败
模块 : mediasoup
文件 : srcWorker.ts
方法 : 
export class Worker extends EnhancedEventEmitter
{

    ...

	constructor(
		{
			logLevel,
			logTags,
			rtcMinPort,
			rtcMaxPort,
			dtlsCertificateFile,
			dtlsPrivateKeyFile,
			appData
		}: WorkerSettings)

    ...

}
功能 :
创建 worker 实例,node.js 部分
初始化 spawn 函数的命令以及参数,即设置 spawnBin 以及 spawnArgs
通过 spawn 启动 mediasoup-worker 进程
	spawn() 使用指定的命令行参数创建新进程
	启动了 MediaSoup 的 C++ 部分,即 Worker 实例
创建 Channel 实例
创建 PayloadChannel 实例
	Channel、PayloadChannel 实例用于 MediaSoup 的 NodeJs 部分与 C++ 部分通信
设置子进程的监听事件
  • MediaSoup C++ 部分
检查环境变量,确保 C++ 部分由 Node.js 启动

模块 : mediasoup
文件 : workersrclib.cpp
函数 :
extern "C" int run_worker(
  int argc,
  char* argv[],
  const char* version,
  int consumerChannelFd,
  int producerChannelFd,
  int payloadConsumeChannelFd,
  int payloadProduceChannelFd)
功能 :
启动 Worker 实例的 C++ 进程,C++ 部分
1. 初始化 libuv 事件库
2. 初始化 Channel Socket 通道
3. 初始化 PayloadChannel Socket 通道
4. 初始化日志实例
5. 解析命令行参数并保存在 Settings 类中
6. 大小端检测
7. 检测系统位 32/64
8. 打印配置与版本信息
9. 初始化
   Openssl,void DepOpenSSL::ClassInit()
   SRTP,void DepLibSRTP::ClassInit()
   SCTP,void DepUsrSCTP::ClassInit()
   WebRTC,void DepLibWebRTC::ClassInit()
   Crypto,void Crypto::ClassInit()
   DtlsTransport,void DtlsTransport::ClassInit()
   SrtpSession,void SrtpSession::ClassInit()
   ChannelNotifier,void ChannelNotifier::ClassInit(Channel::ChannelSocket* channel)
   PayloadChannelNotifier,
       void PayloadChannelNotifier::ClassInit(PayloadChannel::PayloadChannelSocket* payloadChannel)
10. 运行 Worker 实例
   设置 channel 对应的监听者
   设置 payloadChannel 对应的监听者
   创建信号处理句柄
   创建 SCTP 的检查对象
   发送 "running" 事件,通知 NodeJs 部分,C++ 部分的 Worker 已经启动

【3】Router 的创建过程分析

  • MediaSoup-Demo
模块 : mediasoup-demoserver
文件 : serverserver.js
方法 :
async function runProtooWebSocketServer()
功能 :
创建并启动一个 WebSocket 服务端,监听 'connectionrequest' 事件,处理客户端连接请求
1. 基于 HTTPS 服务器实例创建 WebSocket 实例
2. 监听 'connectionrequest' 事件,处理客户端连接请求
    解析请求并根据 roomId 创建对应的 room 实例
模块 : mediasoup-demoserver
文件 : serverserver.js
方法 :
async function getOrCreateRoom({ roomId })
功能 :
获取或创建一个 Room 实例
1. 尝试根据 roomId 获取 room 实例
2. 若 room 实例不存在则
    获取 MediaSoup Worker 实例
    创建 Room 实例
    缓存 Room 实例,Room 实例监听 'close' 事件
模块 : mediasoup-demoserver
文件 : serverserver.js
方法 :
function getMediasoupWorker()
功能 :
按照次序选择一个保存的 Worker 实例
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
class Room extends EventEmitter
{

    ...

    static async create({ mediasoupWorker, roomId })

    ...

}
功能 :
创建一个 Room 实例
1. 创建一个房间,基于 protoo-server 模块
2. 获取 Router 对应的媒体编解码器配置信息
3. 创建一个 Mediasoup Router 实例
    此处会调用 MediaSoup 模块的对应的方法
4. 创建一个 AudioLevelObserver 实例,用于观察音量变化
5. 创建 Bot 实例,用于传递数据
6. 构造并返回一个 Room 实例(NodeJs 对象)
  • MediaSoup NodeJS 部分
模块 : mediasoup
文件 : srcWorker.ts
方法 :
export class Worker extends EnhancedEventEmitter
{

    ...

    async createRouter(
        {
            mediaCodecs,
            appData = {}
        }: RouterOptions = {}): Promise<Router>

    ...

}
功能 :
创建一个 Router 实例
1. 基于给定的媒体编解码器以及 RTP 能力生成 Router 的 RTP 能力描述
2. 发送请求,通知 C++ 部分创建 Router 实例
3. 创建 NodeJs 部分的 Router 实例
4. 缓存 Router 实例
5. Router 实例监听 '@close' 事件
6. 发送 'newrouter' 事件通知, 传送 Router 实例
模块 : mediasoup
文件 : srcortc.ts
方法 :
export function generateRouterRtpCapabilities(
	mediaCodecs: RtpCodecCapability[] = []
): RtpCapabilities
功能 :
基于给定的媒体编解码器以及 RTP 能力生成 Router 的 RTP 能力描述
  • MediaSoup C++ 部分
模块 : mediasoup
文件 : workersrcWorker.cpp
方法 :
inline void Worker::OnChannelRequest(Channel::ChannelSocket* /*channel*/, Channel::ChannelRequest* request)
{

    ...

    case Channel::ChannelRequest::MethodId::WORKER_CREATE_ROUTER:
    {
        
    }

    ...

}
功能 :
处理 worker.createRouter 请求,C++ 部分创建 Router 实例
1. 确认 Json 中存在 routerId 并且 
        std::unordered_map<std::string, RTC::Router*> mapRouters;
        中没有对应的 Router 实例
2. 创建 RTC::Router 实例并缓存在
        std::unordered_map<std::string, RTC::Router*> mapRouters;
        中并发送响应信息
模块 : mediasoup
文件 : workersrcWorker.cpp
方法 :
void Worker::SetNewRouterIdFromInternal(json& internal, std::string& routerId) const
功能 :
确认 Json 中存在 routerId 并且 mapRouters 中没有对应的 Router 实例
模块 : mediasoup
文件 : workersrcRTCRouter.cpp
方法 :
namespace RTC
{

    ...

	Router::Router(const std::string& id) : id(id)
	{
		MS_TRACE();
	}

    ...

}
功能 :
创建 Router 实例

【4】WebRtcTransport 的创建过程分析

  • MediaSoup-Demo
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
handleProtooConnection({ peerId, consume, protooWebSocketTransport })
功能 :
处理连接
1. 检查房间中是否已经存在 peerId 对应的 peer 实例
2. 在房间中创建一个 Peer 对端连接
3. 初始化 Peer 对端实例的 data 属性
4. Peer 对端连接监听处理 'request' 事件
5. Peer 对端连接监听处理 'close' 事件
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
async _handleProtooRequest(peer, request, accept, reject)
功能 :
处理请求

处理 'createWebRtcTransport' 请求
1. 解析 request 的 data
2. 初始化 webrtc 传输配置参数
3. 创建 WebRTC 传输实例并设置监听事件
    此处调用 mediasoup 模块中对应的方法
4. peer data 缓存 transport 实例信息
5. 响应 transport 实例信息
  • MediaSoup NodeJS 部分
模块 : mediasoup
文件 : srcRouter.ts
方法 :
async createWebRtcTransport(
    {
        listenIps,
        port,
        enableUdp = true,
        enableTcp = false,
        preferUdp = false,
        preferTcp = false,
        initialAvailableOutgoingBitrate = 600000,
        enableSctp = false,
        numSctpStreams = { OS: 1024, MIS: 1024 },
        maxSctpMessageSize = 262144,
        sctpSendBufferSize = 262144,
        appData = {}
    }: WebRtcTransportOptions
): Promise<WebRtcTransport>
功能 :
创建 WebRtcTransport 实例
1. 获取待监听的 IP 地址
2. 构造请求数据
3. 发送 'router.createWebRtcTransport' 请求,C++ 部分创建 WebRtcTransport 实例
4. 创建 NodeJs 端的 WebRtcTransport 实例
    初始化 WebRTC 相关的属性配置
    处理 WebRTC 通知
5. 当前 Router 实例缓存新建的 Transport 实例
6. 设置 Transport 实例的监听事件
7. 发送 'newtransport' 通知并传递 transport 实例
  • MediaSoup C++ 部分
模块 : mediasoup
文件 : workersrcWorker.cpp
方法 :
inline void Worker::OnChannelRequest(Channel::ChannelSocket* /*channel*/, Channel::ChannelRequest* request)
{

    ...

    // Any other request must be delivered to the corresponding Router.
    default:
    {
        try
        {
            // 获取 Router 实例
            RTC::Router* router = GetRouterFromInternal(request->internal);
            // Router 实例处理请求
            router->HandleRequest(request);
        }
        catch (const MediaSoupTypeError& error)
        {

        }

        break;
    }

    ...

}
功能 :
处理 NodeJs 端发送的请求,转发给对应的 Router 实例处理

处理 Channel::ChannelRequest::MethodId::ROUTER_CREATE_WEBRTC_TRANSPORT
1. 确定请求中存在 transportId 项
2. 确定 mapTransports 不存在与 transportId 对应的 RTC::Transport 实例
3. 创建 WebRtcTransport 实例并缓存在 mapTransports 中
4. 构造反馈信息并发送响应数据

【5】Producer 的创建过程分析

向 mediasoup-demoserver 发送 createWebRtcTransport 请求,指明 transport 类型为发布者
解析响应获取 transport 参数信息
创建发送媒体的 Transport 实例, 即 _sendTransport
在 Transport 实例创建完毕后调用 _handleHandler(): void 方法
    _handleHandler(): void 方法中监听 '@connect' 事件,在 '@connect' 事件触发后触发 'connect' 事件
    Transport 实例监听 'connect' 事件,在事件触发后发送 'connectWebRtcTransport' 请求给 mediasoup-demoserver
    Transport 实例监听 'produce' 事件,在事件触发后发送 'produce' 请求给 mediasoup-demoserver
在开启麦克风、网络摄像头、录屏功能后调用 this._sendTransport.produce 方法
    模块 : mediasoup-client
    文件 : srcTransport.ts
    方法 :
    async produce(
        {
            track,
            encodings,
            codecOptions,
            codec,
            stopTracks = true,
            disableTrackOnPause = true,
            zeroRtpOnPause = false,
            appData = {}
        }: ProducerOptions = {}
    ): Promise<Producer>
    中调用
        const { localId, rtpParameters, rtpSender } = await this._handler.send(
            {
                track,
                encodings : normalizedEncodings,
                codecOptions,
                codec
            });
        模块 : mediasoup-client
        文件 : srchandlersChrome74.ts
        方法 :
        async send(
            { track, encodings, codecOptions, codec }: HandlerSendOptions
        ): Promise<HandlerSendResult>
        中调用
            if (!this._transportReady)
                await this._setupTransport({ localDtlsRole: 'server', localSdpObject });
                模块 : mediasoup-client
                文件 : srchandlersChrome74.ts
                方法 :
                private async _setupTransport(
                    {
                        localDtlsRole,
                        localSdpObject
                    }:
                    {
                        localDtlsRole: DtlsRole;
                        localSdpObject?: any;
                    }
                ): Promise<void>
                中调用
                    await this.safeEmitAsPromise('@connect', { dtlsParameters });
                    触发 '@connect' 事件

    同时,this._sendTransport.produce 方法中触发 'produce' 事件

'produce' 请求的处理过程分析

  • MediaSoup-Demo
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
handleProtooConnection({ peerId, consume, protooWebSocketTransport })
功能 :
处理连接
1. 检查房间中是否已经存在 peerId 对应的 peer 实例
2. 在房间中创建一个 Peer 对端连接
3. 初始化 Peer 对端实例的 data 属性
4. Peer 对端连接监听处理 'request' 事件
5. Peer 对端连接监听处理 'close' 事件
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
async _handleProtooRequest(peer, request, accept, reject)
功能 :
处理请求

处理 'produce' 请求
1. 解析请求数据
2. 获取 transport 对象实例
3. 生成发布者
    此处调用 mediasoup 模块中对应的方法
4. 缓存发布者实例
5. 设置发布者实例监听的事件
6. 发送响应,发布者 ID
7. 遍历房间中的参与者,为每个发布端创建一个 server 端的消费者实例
8. 将发布者实例添加到 _audioLevelObserver 中
  • MediaSoup NodeJS 部分
模块 : mediasoup
文件 : srcTransport.ts
方法 :
async produce(
    {
        id = undefined,
        kind,
        rtpParameters,
        paused = false,
        keyFrameRequestDelay,
        appData = {}
    }: ProducerOptions
): Promise<Producer>
功能 :
创建一个发布者
1. 检查参数有效性
2. 验证 RTP 参数有效性,获取 RTP 相关参数设置
3. 发送 'transport.produce' 请求,请求 C++ 部分创建发布者实例
4. 创建 NodeJs 端的发布者实例并设置监听事件
5. 触发 'newproducer' 事件
  • MediaSoup C++ 部分
模块 : mediasoup
文件 : workersrcRTCRouter.cpp
方法 :
void Router::HandleRequest(Channel::ChannelRequest* request)
功能 :
Router 实例处理对应的请求

处理 default 部分
获取 transport 实例
调用 transport 实例及其子类的 HandleRequest 方法处理请求

模块 : mediasoup
文件 : workersrcRTCRouter.cpp
方法 :
RTC::Transport* Router::GetTransportFromInternal(json& internal) const
功能 :
从缓存获取 transport 实例
1. 确定请求的 Json 数据中是否包含 transportId 字段
2. 确定通过 transportId 字段可以找到对应 transport 实例


模块 : mediasoup
文件 : workersrcRTCTransport.cpp
方法 :
void Transport::HandleRequest(Channel::ChannelRequest* request)
功能 :
处理请求
Channel::ChannelRequest::MethodId::TRANSPORT_PRODUCE

【6】Consumer 的创建过程分析

向 mediasoup-demoserver 发送 createWebRtcTransport 请求,指明 transport 类型为消费者
解析响应获取 transport 参数信息
创建接收媒体的 Transport 实例, 即 _recvTransport
在 Transport 实例创建完毕后调用 _handleHandler(): void 方法
    _handleHandler(): void 方法中监听 '@connect' 事件,在 '@connect' 事件触发后触发 'connect' 事件
    Transport 实例监听 'connect' 事件,在事件触发后发送 'connectWebRtcTransport' 请求给 mediasoup-demoserver
在 WebSocket 客户端接收到 'newConsumer'(新成员加入房间) 请求,将会调用 this._recvTransport.consume 方法
    模块 : mediasoup-client
    文件 : srcTransport.ts
    方法 :
    async consume(
        {
            id,
            producerId,
            kind,
            rtpParameters,
            appData = {}
        }: ConsumerOptions
    ): Promise<Consumer>
    中调用
        const { localId, rtpReceiver, track } =
            await this._handler.receive({ trackId: id, kind, rtpParameters });
            模块 : mediasoup-client
            文件 : srchandlersChrome74.ts
            方法 :
            async receive(
                { trackId, kind, rtpParameters }: HandlerReceiveOptions
            ): Promise<HandlerReceiveResult>
            中调用
                if (!this._transportReady)
                    await this._setupTransport({ localDtlsRole: 'client', localSdpObject });
                    模块 : mediasoup-client
                    文件 : srchandlersChrome74.ts
                    方法 :
                    private async _setupTransport(
                        {
                            localDtlsRole,
                            localSdpObject
                        }:
                        {
                            localDtlsRole: DtlsRole;
                            localSdpObject?: any;
                        }
                    ): Promise<void>
                    中调用
                        await this.safeEmitAsPromise('@connect', { dtlsParameters });
                        触发 '@connect' 事件

【7】WebRtcTransport 的连接过程分析

  • MediaSoup-Demo
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
handleProtooConnection({ peerId, consume, protooWebSocketTransport })
功能 :
处理连接
1. 检查房间中是否已经存在 peerId 对应的 peer 实例
2. 在房间中创建一个 Peer 对端连接
3. 初始化 Peer 对端实例的 data 属性
4. Peer 对端连接监听处理 'request' 事件
5. Peer 对端连接监听处理 'close' 事件
模块 : mediasoup-demoserver
文件 : serverlibRoom.js
方法 :
async _handleProtooRequest(peer, request, accept, reject)
功能 :
处理请求

处理 'connectWebRtcTransport' 请求
1. 解析请求数据
2. 获取 transport 对象实例
3. 建立连接
    此处调用 mediasoup 模块中对应的方法
4. 发送响应
  • MediaSoup NodeJS 部分
模块 : mediasoup
文件 : srcWebRtcTransport.ts
方法 :
async connect({ dtlsParameters }: { dtlsParameters: DtlsParameters }): Promise<void>
功能 :
与 C++ 端的 WebRtcTransport 实例建立连接
1. 构造请求数据
2. 发送 'transport.connect' 请求,连接 C++ 部分对应的 WebRtcTransport 实例
3. 设置当前 transport 实例的 DTLS 角色
  • MediaSoup C++ 部分
模块 : mediasoup
文件 : workersrcWorker.cpp
方法 :
inline void Worker::OnChannelRequest(Channel::ChannelSocket* /*channel*/, Channel::ChannelRequest* request)
{

    ...

    // Any other request must be delivered to the corresponding Router.
    default:
    {
        try
        {
            // 获取 Router 实例
            RTC::Router* router = GetRouterFromInternal(request->internal);
            // Router 实例处理请求
            router->HandleRequest(request);
        }
        catch (const MediaSoupTypeError& error)
        {

        }

        break;
    }

    ...

}
功能 :
处理 NodeJs 端发送的请求,转发给对应的 Router 实例处理
模块 : mediasoup
文件 : workersrcRTCRouter.cpp
方法 :
void Router::HandleRequest(Channel::ChannelRequest* request)
功能 :
Router 实例处理对应的请求

处理 default 部分
获取 transport 实例
调用 transport 实例及其子类的 HandleRequest 方法处理请求
模块 : mediasoup
文件 : workersrcRTCRouter.cpp
方法 :
RTC::Transport* Router::GetTransportFromInternal(json& internal) const
功能 :
从缓存获取 transport 实例
1. 确定请求的 Json 数据中是否包含 transportId 字段
2. 确定通过 transportId 字段可以找到对应 transport 实例
模块 : mediasoup
文件 : workersrcRTCWebRtcTransport.cpp
方法 :
void WebRtcTransport::HandleRequest(Channel::ChannelRequest* request)
功能 :
处理请求
Channel::ChannelRequest::MethodId::TRANSPORT_CONNECT
Channel::ChannelRequest::MethodId::TRANSPORT_RESTART_ICE

附录

【附录 - A】MediaSoup-Demo App 项目中相关知识点总结

  • React 技术栈系列教程

React 技术栈系列教程icon-default.png?t=M4ADhttps://www.ruanyifeng.com/blog/2016/09/react-technology-stack.html

  • React 入门实例教程

React 入门实例教程icon-default.png?t=M4ADhttps://www.ruanyifeng.com/blog/2015/03/react.html

  • Redux 相关知识点总结

Redux 入门教程(一):基本用法https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.htmlRedux 入门教程(二):中间件与异步操作https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.htmlRedux 入门教程(三):React-Redux 的用法https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

参考致谢

本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】mediasoup v3 API

【2】mediaSoup总体架构分析

【3】【流媒体】Mediasoup库的架构(C++部分)

【4】【流媒体】Media soup源码分析(一)信令的传输过程

【5】【流媒体】Media soup源码分析(二)Router的建立

【6】【流媒体】Media soup源码分析(三)传输通道的建立

最后

以上就是畅快野狼为你收集整理的【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析的全部内容,希望文章能够帮你解决【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析【网络通信 -- WebRTC】MediaSoup 源码分析 -- 总体架构分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部