mediasoup源码分析(三)channel创建及信令交互

2024-06-18 11:20

本文主要是介绍mediasoup源码分析(三)channel创建及信令交互,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

mediasoup源码分析--channel创建及信令交互

    • 概述
    • 跨职能图
    • 业务流程图
    • 代码剖析

概述

在golang实现mediasoup的tcp服务及channel通道一文中,已经介绍过信令服务中tcp和channel的创建,本文主要讲解c++中mediasoup的channel创建,以及信令服务和mediasoup服务如何交互

跨职能图

c92bb199ca71184775fa8bea149e201.png

业务流程图

image.png

数据发送有两种方式:
应用层发送的request最后被封装在Requst对象中,其中包含着"id",因为Request对象中包含着Channel::UnixStreamSocket对象,所以可以直接调用Request::Accept()将处理后的结果告诉应用层进程。
Worker进程也可以主动给应用层进程发送消息,通过Notifier::Emit()即可以给应用进程发送消息,Notifier类中有Channel::UnixStreamSocket,所以直接调用Channel::UnixStreamSocket::Send()就可以发送消息。Notifier类内部的数据成员和函数成员都是静态的,所以在任意位置可以直接通过Channel::Notifier::Emit()函数发送消息。

代码剖析

1.channel创建

int main(int argc, char* argv[])
{// Ensure we are called by our Node library.if (argc == 1){std::cerr << "ERROR: you don't seem to be my real father" << std::endl;std::_Exit(EXIT_FAILURE);}std::string id = std::string(argv[1]);std::string ip = std::string(argv[2]);int port = atoi(argv[3]);int iperfPort = atoi(argv[4]);// Initialize libuv stuff (we need it for the Channel).DepLibUV::ClassInit();//..........省略部分代码..............// Set the Channel socket (this will be handled and deleted by the Worker).printf("new Channel to %s:%d\n",ip.c_str(),port);auto* channel = new Channel::UnixStreamSocket(ip,port);//..........省略部分代码..............try{// Run the Worker.Worker worker(id,channel);// Worker ended.destroy();exitSuccess();}catch (const MediaSoupError& error){MS_ERROR_STD("failure exit: %s", error.what());destroy();exitWithError();}
}

UnixStreamSocket构造函数

UnixStreamSocket::UnixStreamSocket(const std::string& ip,int port) : ::UnixStreamSocket::UnixStreamSocket(ip,port, MaxSize)
{MS_TRACE_STD();// Create the JSON reader.{Json::CharReaderBuilder builder;Json::Value settings = Json::nullValue;Json::Value invalidSettings;builder.strictMode(&settings);MS_ASSERT(builder.validate(&invalidSettings), "invalid Json::CharReaderBuilder");this->jsonReader = builder.newCharReader();}// Create the JSON writer.{Json::StreamWriterBuilder builder;Json::Value invalidSettings;builder["commentStyle"]            = "None";builder["indentation"]             = "";builder["enableYAMLCompatibility"] = false;builder["dropNullPlaceholders"]    = false;MS_ASSERT(builder.validate(&invalidSettings), "invalid Json::StreamWriterBuilder");this->jsonWriter = builder.newStreamWriter();}
}

跳转到handles\UnixStreamSocket.cpp下

UnixStreamSocket::UnixStreamSocket( const std::string& ip,int port,size_t bufferSize) : bufferSize(bufferSize)
{printf("::UnixStreamSocket::UnixStreamSocket\n");MS_TRACE_STD();int err;this->uvHandle       = new uv_tcp_t;this->uvHandle->data = (void*)this;err = uv_tcp_init(DepLibUV::GetLoop(), this->uvHandle);if (err != 0){delete this->uvHandle;this->uvHandle = nullptr;printf("uv_tcp_init() failed: %s\n", uv_strerror(err));MS_THROW_ERROR_STD("uv_tcp_init() failed: %s", uv_strerror(err));}struct sockaddr_in dest;uv_ip4_addr(ip.c_str(), port, &dest);this->connect = new uv_connect_t;printf("will connect to %s:%d\n",ip.c_str(),port);err = uv_tcp_connect(this->connect, this->uvHandle, (const struct sockaddr*)&dest, onConnect);if (err != 0){delete this->uvHandle;this->uvHandle = nullptr;printf("uv_tcp_connect() failed: %s\n", uv_strerror(err));MS_THROW_ERROR_STD("uv_tcp_connect() failed: %s", uv_strerror(err));}// Start reading.err = uv_read_start(reinterpret_cast<uv_stream_t*>(this->uvHandle),static_cast<uv_alloc_cb>(onAlloc),static_cast<uv_read_cb>(onRead));if (err != 0){uv_close(reinterpret_cast<uv_handle_t*>(this->uvHandle), static_cast<uv_close_cb>(onClose));MS_THROW_ERROR_STD("uv_read_start() failed: %s", uv_strerror(err));}// NOTE: Don't allocate the buffer here. Instead wait for the first uv_alloc_cb().
}

代码中的uv_read_start接口中onRead回调

    err = uv_read_start(reinterpret_cast<uv_stream_t*>(this->uvHandle),static_cast<uv_alloc_cb>(onAlloc),static_cast<uv_read_cb>(onRead));

跳转到onRead中

inline static void onRead(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
{auto* socket = static_cast<UnixStreamSocket*>(handle->data);if (socket == nullptr)return;socket->OnUvRead(nread, buf);
}

OnUvRead中调用UserOnUnixStreamRead

	void UnixStreamSocket::UserOnUnixStreamRead(){MS_TRACE_STD();// Be ready to parse more than a single message in a single TCP chunk.while (true){if (IsClosed())return;size_t readLen  = this->bufferDataLen - this->msgStart;char* jsonStart = nullptr;size_t jsonLen;int nsRet = netstring_read(reinterpret_cast<char*>(this->buffer + this->msgStart), readLen, &jsonStart, &jsonLen);//.............省略部分代码..............// If here it means that jsonStart points to the beginning of a JSON string// with jsonLen bytes length, so recalculate readLen.readLen =reinterpret_cast<const uint8_t*>(jsonStart) - (this->buffer + this->msgStart) + jsonLen + 1;Json::Value json;std::string jsonParseError;if (this->jsonReader->parse((const char*)jsonStart, (const char*)jsonStart + jsonLen, &json, &jsonParseError)){Channel::Request* request = nullptr;try{request = new Channel::Request(this, json);}catch (const MediaSoupError& error){MS_ERROR_STD("discarding wrong Channel request");}if (request != nullptr){// Notify the listener.this->listener->OnChannelRequest(this, request);// Delete the Request.delete request;}//.............省略部分代码.................}}

channel创建完成,至此,跳转到worker.cpp中的OnChannelRequest接口。mediasoup监听channel信令并根据request->methodId分类处理
根据request->methodId,分别执行不同的业务
request->methodId有如下分类

	std::unordered_map<std::string, Request::MethodId> Request::string2MethodId ={{ "worker.dump",                       Request::MethodId::WORKER_DUMP                          },{ "worker.updateSettings",             Request::MethodId::WORKER_UPDATE_SETTINGS               },{ "worker.createRouter",               Request::MethodId::WORKER_CREATE_ROUTER                 },{ "router.close",                      Request::MethodId::ROUTER_CLOSE                         },{ "router.dump",                       Request::MethodId::ROUTER_DUMP                          },{ "router.createWebRtcTransport",      Request::MethodId::ROUTER_CREATE_WEBRTC_TRANSPORT       },{ "router.createPlainRtpTransport",    Request::MethodId::ROUTER_CREATE_PLAIN_RTP_TRANSPORT    },{ "router.createProducer",             Request::MethodId::ROUTER_CREATE_PRODUCER               },{ "router.createConsumer",             Request::MethodId::ROUTER_CREATE_CONSUMER               },{ "router.setAudioLevelsEvent",        Request::MethodId::ROUTER_SET_AUDIO_LEVELS_EVENT        },{ "transport.close",                   Request::MethodId::TRANSPORT_CLOSE                      },{ "transport.dump",                    Request::MethodId::TRANSPORT_DUMP                       },{ "transport.getStats",                Request::MethodId::TRANSPORT_GET_STATS                  },{ "transport.setRemoteDtlsParameters", Request::MethodId::TRANSPORT_SET_REMOTE_DTLS_PARAMETERS },{ "transport.setRemoteParameters",     Request::MethodId::TRANSPORT_SET_REMOTE_PARAMETERS      },{ "transport.setMaxBitrate",           Request::MethodId::TRANSPORT_SET_MAX_BITRATE            },{ "transport.changeUfragPwd",          Request::MethodId::TRANSPORT_CHANGE_UFRAG_PWD           },{ "transport.startMirroring",          Request::MethodId::TRANSPORT_START_MIRRORING            },{ "transport.stopMirroring",           Request::MethodId::TRANSPORT_STOP_MIRRORING             },{ "producer.close",                    Request::MethodId::PRODUCER_CLOSE                       },{ "producer.dump",                     Request::MethodId::PRODUCER_DUMP                        },{ "producer.getStats",                 Request::MethodId::PRODUCER_GET_STATS                   },{ "producer.pause",                    Request::MethodId::PRODUCER_PAUSE                       },{ "producer.resume" ,                  Request::MethodId::PRODUCER_RESUME                      },{ "producer.setPreferredProfile",      Request::MethodId::PRODUCER_SET_PREFERRED_PROFILE       },{ "consumer.close",                    Request::MethodId::CONSUMER_CLOSE                       },{ "consumer.dump",                     Request::MethodId::CONSUMER_DUMP                        },{ "consumer.getStats",                 Request::MethodId::CONSUMER_GET_STATS                   },{ "consumer.enable",                   Request::MethodId::CONSUMER_ENABLE                      },{ "consumer.pause",                    Request::MethodId::CONSUMER_PAUSE                       },{ "consumer.resume",                   Request::MethodId::CONSUMER_RESUME                      },{ "consumer.setPreferredProfile",      Request::MethodId::CONSUMER_SET_PREFERRED_PROFILE       },{ "consumer.setEncodingPreferences",   Request::MethodId::CONSUMER_SET_ENCODING_PREFERENCES    },{ "consumer.requestKeyFrame",          Request::MethodId::CONSUMER_REQUEST_KEY_FRAME           }};

下一章节介绍mediasoup如何将信令返回值及其他通知信息推送到信令服务,敬请期待!

这篇关于mediasoup源码分析(三)channel创建及信令交互的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1071935

相关文章

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

MySQL 临时表创建与使用详细说明

《MySQL临时表创建与使用详细说明》MySQL临时表是存储在内存或磁盘的临时数据表,会话结束时自动销毁,适合存储中间计算结果或临时数据集,其名称以#开头(如#TempTable),本文给大家介绍M... 目录mysql 临时表详细说明1.定义2.核心特性3.创建与使用4.典型应用场景5.生命周期管理6.注

MySQL的触发器全解析(创建、查看触发器)

《MySQL的触发器全解析(创建、查看触发器)》MySQL触发器是与表关联的存储程序,当INSERT/UPDATE/DELETE事件发生时自动执行,用于维护数据一致性、日志记录和校验,优点包括自动执行... 目录触发器的概念:创建触www.chinasem.cn发器:查看触发器:查看当前数据库的所有触发器的定

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

MySQL中读写分离方案对比分析与选型建议

《MySQL中读写分离方案对比分析与选型建议》MySQL读写分离是提升数据库可用性和性能的常见手段,本文将围绕现实生产环境中常见的几种读写分离模式进行系统对比,希望对大家有所帮助... 目录一、问题背景介绍二、多种解决方案对比2.1 原生mysql主从复制2.2 Proxy层中间件:ProxySQL2.3

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl