正文目录
JS部分
JS部分的重点是Worker.js
1、先看整体
从Worker.JS这个文件来看,上面构造了一些变量,如process、channel等,下面是一个Worker的类
2、展开Worker
展开Worker类可以看见它有11个函数,分别是
1.constructor()——构造函数
2.get pid()——获得Worker进程的ID
3.get closed()——确认Worker是否关闭
4.get appData()——返回custom的数据
5.set appData()——当设置无效时抛出异常信息
6.get observer()——开启观察者模式
7.close()——关闭Worker
8.async dump()——转存Worker
9.async getResourceUsage()——获得worker进程资源使用信息
10.async updateSettings()——更新设置
11.async createRouter()——创建房间
3、constructor
我们的重点是channel怎么建立的,所以重点关注第1个构造函数constructor()
this._child = child_process_1.spawn(
// command
spawnBin,
// args
spawnArgs,
// options
{
env: {
MEDIASOUP_VERSION: '3.6.7'
},
detached: false,
// fd 0 (stdin) : Just ignore it.
// fd 1 (stdout) : Pipe it for 3rd libraries that log their own stuff.
// fd 2 (stderr) : Same as stdout.
// fd 3 (channel) : Producer Channel fd.
// fd 4 (channel) : Consumer Channel fd.
// fd 5 (channel) : Producer PayloadChannel fd.
// fd 6 (channel) : Consumer PayloadChannel fd.
stdio: ['ignore', 'pipe', 'pipe', 'pipe', 'pipe', 'pipe', 'pipe'],
windowsHide: true
});
this._pid = this._child.pid;
this._channel = new Channel_1.Channel({
producerSocket: this._child.stdio[3],
consumerSocket: this._child.stdio[4],
pid: this._pid
});
this._payloadChannel = new PayloadChannel_1.PayloadChannel({
// NOTE: TypeScript does not like more than 5 fds.
// @ts-ignore
producerSocket: this._child.stdio[5],
// @ts-ignore
consumerSocket: this._child.stdio[6]
});
spawn里标准io口有7个参数,分别是标准输入、标准输出、标准错误、以及4个通道,源码中对标准输入规定的是ignore,其它6个参数是pipe(管道),这里要注意的是,这个管道并不是Linux进程间通信的匿名管道或有名管道,它是UnixSocketPair,因为只有UnixSocketPair才是全双工通信,从代码中我们也能看出它是全双工的,而匿名(有名)管道是半双工通信
接着重点是 this._channel = new Channel_1.Channel
,它创建了一个channel,并传入了3个参数,分别是stdio[3]和stdio[4],以及pid(因为可能会有多个Worker,而一个进程对应一个Worker,所以需要知道每个进程的ID),这样通过这个channel,JS部分便能和C++部分通信了
4、channel的建立
创建这个channel会调用channel.js里的构造函数,我们来看看channel.js里的代码
this._consumerSocket.on('data', (buffer) => {
if (!this._recvBuffer) {
this._recvBuffer = buffer;
}
else {
this._recvBuffer = Buffer.concat([this._recvBuffer, buffer], this._recvBuffer.length + buffer.length);
}
if (this._recvBuffer.length > NS_PAYLOAD_MAX_LEN) {
logger.error('receiving buffer is full, discarding all data into it');
// Reset the buffer and exit.
this._recvBuffer = undefined;
return;
}
while (true) // eslint-disable-line no-constant-condition
{
let nsPayload;
try {
nsPayload = netstring.nsPayload(this._recvBuffer);
}
catch (error) {
logger.error('invalid netstring data received from the worker process: %s', String(error));
// Reset the buffer and exit.
this._recvBuffer = undefined;
return;
}
// Incomplete netstring message.
if (nsPayload === -1)
return;
try {
// We can receive JSON messages (Channel messages) or log strings.
switch (nsPayload[0]) {
// 123 = '{' (a Channel JSON messsage).
case 123:
this._processMessage(JSON.parse(nsPayload.toString('utf8')));
break;
// 68 = 'D' (a debug log).
case 68:
logger.debug(`[pid:${pid}] ${nsPayload.toString('utf8', 1)}`);
break;
// 87 = 'W' (a warn log).
case 87:
logger.warn(`[pid:${pid}] ${nsPayload.toString('utf8', 1)}`);
break;
// 69 = 'E' (an error log).
case 69:
logger.error(`[pid:${pid} ${nsPayload.toString('utf8', 1)}`);
break;
// 88 = 'X' (a dump log).
case 88:
// eslint-disable-next-line no-console
console.log(nsPayload.toString('utf8', 1));
break;
default:
// eslint-disable-next-line no-console
console.warn(`worker[pid:${pid}] unexpected data: %s`, nsPayload.toString('utf8', 1));
}
}
catch (error) {
logger.error('received invalid message from the worker process: %s', String(error));
}
// Remove the read payload from the buffer.
this._recvBuffer =
this._recvBuffer.slice(netstring.nsLength(this._recvBuffer));
if (!this._recvBuffer.length) {
this._recvBuffer = undefined;
return;
}
}
});
在channel.js中,上面这片代码启动了Socket的侦听函数,当C++传来数据时,会促发recvBuffer接收数据,然后进入while循环处理数据,做出相应的处理
5、JS部分的总结
经过上述步骤后,便拿到了SocketPair一端的文件描述符,实际不管是Socket还是普通文件,都是通过文件描述符的方式来进行的操作
C++部分
1、main流程图
main主函数部分的模块可分为下图的几个步骤,在最新版V3的源码中,main创建了两个Socket,分别是Channel和PayloadChannel
2、main.cpp
#define MS_CLASS "mediasoup-worker"
// #define MS_LOG_DEV_LEVEL 3
#include "common.hpp"
#include "DepLibSRTP.hpp"
#include "DepLibUV.hpp"
#include "DepLibWebRTC.hpp"
#include "DepOpenSSL.hpp"
#include "DepUsrSCTP.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "Settings.hpp"
#include "Utils.hpp"
#include "Worker.hpp"
#include "Channel/Notifier.hpp"
#include "Channel/UnixStreamSocket.hpp"
#include "PayloadChannel/Notifier.hpp"
#include "PayloadChannel/UnixStreamSocket.hpp"
#include "RTC/DtlsTransport.hpp"
#include "RTC/SrtpSession.hpp"
#include <uv.h>
#include <cerrno>
#include <csignal> // sigaction()
#include <cstdlib> // std::_Exit(), std::genenv()
#include <iostream> // std::cerr, std::endl
#include <map>
#include <string>
static constexpr int ConsumerChannelFd{ 3 };
static constexpr int ProducerChannelFd{ 4 };
static constexpr int PayloadConsumerChannelFd{ 5 };
static constexpr int PayloadProducerChannelFd{ 6 };
void IgnoreSignals();
int main(int argc, char* argv[])
{
// Ensure we are called by our Node library.
if (std::getenv("MEDIASOUP_VERSION") == nullptr)
{
MS_ERROR_STD("you don't seem to be my real father!");
std::_Exit(EXIT_FAILURE);
}
std::string version = std::getenv("MEDIASOUP_VERSION");
// Initialize libuv stuff (we need it for the Channel).
DepLibUV::ClassInit();
// Channel socket (it will be handled and deleted by the Worker).
Channel::UnixStreamSocket* channel{ nullptr };
// PayloadChannel socket (it will be handled and deleted by the Worker).
PayloadChannel::UnixStreamSocket* payloadChannel{ nullptr };
try
{
channel = new Channel::UnixStreamSocket(ConsumerChannelFd, ProducerChannelFd);
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("error creating the Channel: %s", error.what());
std::_Exit(EXIT_FAILURE);
}
try
{
payloadChannel =
new PayloadChannel::UnixStreamSocket(PayloadConsumerChannelFd, PayloadProducerChannelFd);
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("error creating the RTC Channel: %s", error.what());
std::_Exit(EXIT_FAILURE);
}
// Initialize the Logger.
Logger::ClassInit(channel);
try
{
Settings::SetConfiguration(argc, argv);
}
catch (const MediaSoupTypeError& error)
{
MS_ERROR_STD("settings error: %s", error.what());
// 42 is a custom exit code to notify "settings error" to the Node library.
std::_Exit(42);
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("unexpected settings error: %s", error.what());
std::_Exit(EXIT_FAILURE);
}
MS_DEBUG_TAG(info, "starting mediasoup-worker process [version:%s]", version.c_str());
#if defined(MS_LITTLE_ENDIAN)
MS_DEBUG_TAG(info, "little-endian CPU detected");
#elif defined(MS_BIG_ENDIAN)
MS_DEBUG_TAG(info, "big-endian CPU detected");
#else
MS_WARN_TAG(info, "cannot determine whether little-endian or big-endian");
#endif
#if defined(INTPTR_MAX) && defined(INT32_MAX) && (INTPTR_MAX == INT32_MAX)
MS_DEBUG_TAG(info, "32 bits architecture detected");
#elif defined(INTPTR_MAX) && defined(INT64_MAX) && (INTPTR_MAX == INT64_MAX)
MS_DEBUG_TAG(info, "64 bits architecture detected");
#else
MS_WARN_TAG(info, "cannot determine 32 or 64 bits architecture");
#endif
Settings::PrintConfiguration();
DepLibUV::PrintVersion();
try
{
// Initialize static stuff.
DepOpenSSL::ClassInit();
DepLibSRTP::ClassInit();
DepUsrSCTP::ClassInit();
DepLibWebRTC::ClassInit();
Utils::Crypto::ClassInit();
RTC::DtlsTransport::ClassInit();
RTC::SrtpSession::ClassInit();
Channel::Notifier::ClassInit(channel);
PayloadChannel::Notifier::ClassInit(payloadChannel);
// Ignore some signals.
IgnoreSignals();
// Run the Worker.
Worker worker(channel, payloadChannel);
// Free static stuff.
DepLibUV::ClassDestroy();
DepLibSRTP::ClassDestroy();
Utils::Crypto::ClassDestroy();
DepLibWebRTC::ClassDestroy();
RTC::DtlsTransport::ClassDestroy();
DepUsrSCTP::ClassDestroy();
// Wait a bit so peding messages to stdout/Channel arrive to the Node
// process.
uv_sleep(200);
std::_Exit(EXIT_SUCCESS);
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("failure exit: %s", error.what());
std::_Exit(EXIT_FAILURE);
}
}
void IgnoreSignals()
{
#ifndef _WIN32
MS_TRACE();
int err;
struct sigaction act; // NOLINT(cppcoreguidelines-pro-type-member-init)
// clang-format off
std::map<std::string, int> ignoredSignals =
{
{ "PIPE", SIGPIPE },
{ "HUP", SIGHUP },
{ "ALRM", SIGALRM },
{ "USR1", SIGUSR1 },
{ "USR2", SIGUSR2 }
};
// clang-format on
act.sa_handler = SIG_IGN; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
act.sa_flags = 0;
err = sigfillset(&act.sa_mask);
if (err != 0)
MS_THROW_ERROR("sigfillset() failed: %s", std::strerror(errno));
for (auto& kv : ignoredSignals)
{
auto& sigName = kv.first;
int sigId = kv.second;
err = sigaction(sigId, &act, nullptr);
if (err != 0)
MS_THROW_ERROR("sigaction() failed for signal %s: %s", sigName.c_str(), std::strerror(errno));
}
#endif
}
3、Channel Socket的建立
提取socket这部分的代码,可以看见源码中创建了2个socket,分别是Channel和PayloadChannel
然后在try{}catch{}表达式中,分别对两个socket对象实例化,这里要注意的是,new中传入的参数Fd,对照JS部分代码的注释可以知道这是固定的3456,而非标准输入0、输出1、错误2
// Channel socket (it will be handled and deleted by the Worker).
Channel::UnixStreamSocket* channel{ nullptr };
// PayloadChannel socket (it will be handled and deleted by the Worker).
PayloadChannel::UnixStreamSocket* payloadChannel{ nullptr };
try
{
channel = new Channel::UnixStreamSocket(ConsumerChannelFd, ProducerChannelFd);
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("error creating the Channel: %s", error.what());
std::_Exit(EXIT_FAILURE);
}
try
{
payloadChannel =
new PayloadChannel::UnixStreamSocket(PayloadConsumerChannelFd, PayloadProducerChannelFd);
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("error creating the RTC Channel: %s", error.what());
std::_Exit(EXIT_FAILURE);
}
4、UnixStreamSocket.cpp
上述 socket 在 new 之后会跳转到 UnixStreamSocket.cpp 执行下面这个函数
UnixStreamSocket::UnixStreamSocket(int consumerFd, int producerFd)
: consumerSocket(consumerFd, NsMessageMaxLen, this), producerSocket(producerFd, NsMessageMaxLen)
{
MS_TRACE_STD();
}
这个函数的关键不是函数体里的内容,而是在参数后面又创建了一个consumerScoket和producerSocket,并传入参数Fd和消息最大长度
5、consumerSocket
接着再进入consumerSocket,可以看到在这个构造函数后面对其父类进行初始化,传入参数fd,缓冲区大小,以及角色
ConsumerSocket::ConsumerSocket(int fd, size_t bufferSize, Listener* listener)
: ::UnixStreamSocket(fd, bufferSize, ::UnixStreamSocket::Role::CONSUMER), listener(listener)
{
MS_TRACE_STD();
}
6、UnixStreamSocket
继续追根溯源,进入UnixStreamSocket的构造函数
在这部分代码中先构造了一个uv_pipe_t的对象,这个是libuv库中的pipe,赋值给uvHandle。然后把对象指针this赋值给私有定义的data
接着是对uv_pipe的初始化,有3个参数,分别是
- 事件循环中的loop
- 刚才创建的对象
- ipc,用于指示pipe是否被用于两个进程之间
初始化结束后,调用uv_pipe_open打开fd所指向的pipe
打开之后进入uv_read_start,这个函数是用来启动读的操作,它同样也有三个参数:
- uvHandle,里面存放的是这个对象本身
- onAlloc,当buffer不足时回调这个函数,便能重新创建一个buffer
- onRead,接收pipe的另一端发送的数据
UnixStreamSocket::UnixStreamSocket(int fd, size_t bufferSize, UnixStreamSocket::Role role)
: bufferSize(bufferSize), role(role)
{
MS_TRACE_STD();
int err;
this->uvHandle = new uv_pipe_t;
this->uvHandle->data = static_cast<void*>(this);
err = uv_pipe_init(DepLibUV::GetLoop(), this->uvHandle, 0);
if (err != 0)
{
delete this->uvHandle;
this->uvHandle = nullptr;
MS_THROW_ERROR_STD("uv_pipe_init() failed: %s", uv_strerror(err));
}
err = uv_pipe_open(this->uvHandle, fd);
if (err != 0)
{
uv_close(reinterpret_cast<uv_handle_t*>(this->uvHandle), static_cast<uv_close_cb>(onClose));
MS_THROW_ERROR_STD("uv_pipe_open() failed: %s", uv_strerror(err));
}
if (this->role == UnixStreamSocket::Role::CONSUMER)
{
// 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().
}
7、onRead
重点是这个onRead函数,接下来看这部分的代码
onRead有三个参数,分别是:
- handle,刚才创建的对象
- nread,读取的数据大小
- buf,存放数据的地方
在函数体里,因为要访问到另一端JS传过来的数据,所以得使用static全局静态函数。具体怎么做呢?首先对handle->data做了强制类型转换,拿到对象里的socket,这样便可在对象里进行操作,最后再调用socket->OnUvRead这个方法
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)
socket->OnUvRead(nread, buf);
}
8、OnUvRead
我们再进入OnUvRead这个函数
在这个函数中首先对nread做了一些判断,如果数据不为空则调用UserOnUnixStreamRead(),注意看它的注释 //Notify the subclass 通知子类
inline void UnixStreamSocket::OnUvRead(ssize_t nread, const uv_buf_t* /*buf*/)
{
MS_TRACE_STD();
if (nread == 0)
return;
// Data received.
if (nread > 0)
{
// Update the buffer data length.
this->bufferDataLen += static_cast<size_t>(nread);
// Notify the subclass.
UserOnUnixStreamRead();
}
// Peer disconnected.
else if (nread == UV_EOF || nread == UV_ECONNRESET)
{
this->isClosedByPeer = true;
// Close local side of the pipe.
Close();
// Notify the subclass.
UserOnUnixStreamSocketClosed();
}
// Some error.
else
{
MS_ERROR_STD("read error, closing the pipe: %s", uv_strerror(nread));
this->hasError = true;
// Close the socket.
Close();
// Notify the subclass.
UserOnUnixStreamSocketClosed();
}
}
9、UserOnUnixStreamRead
进入子类函数,先是netstring_read()函数进行字符串读取,返回0
接着做一个判断,若 nsRet !=0 说明出错,没有读取到数据,后面的switch都是在做错误类型的判断
如果没有出错的话,会先计算读取的字符串,然后再调用OnConsumerSocketMessage()这个函数
void ConsumerSocket::UserOnUnixStreamRead()
{
MS_TRACE_STD();
// Be ready to parse more than a single message in a single chunk.
while (true)
{
if (IsClosed())
return;
size_t readLen = this->bufferDataLen - this->msgStart;
char* msgStart = nullptr;
size_t msgLen;
int nsRet = netstring_read(
reinterpret_cast<char*>(this->buffer + this->msgStart), readLen, &msgStart, &msgLen);
if (nsRet != 0)
{
switch (nsRet)
{
case NETSTRING_ERROR_TOO_SHORT:
{
// Check if the buffer is full.
if (this->bufferDataLen == this->bufferSize)
{
// First case: the incomplete message does not begin at position 0 of
// the buffer, so move the incomplete message to the position 0.
if (this->msgStart != 0)
{
std::memmove(this->buffer, this->buffer + this->msgStart, readLen);
this->msgStart = 0;
this->bufferDataLen = readLen;
}
// Second case: the incomplete message begins at position 0 of the buffer.
// The message is too big, so discard it.
else
{
MS_ERROR_STD(
"no more space in the buffer for the unfinished message being parsed, "
"discarding it");
this->msgStart = 0;
this->bufferDataLen = 0;
}
}
// Otherwise the buffer is not full, just wait.
return;
}
case NETSTRING_ERROR_TOO_LONG:
{
MS_ERROR_STD("NETSTRING_ERROR_TOO_LONG");
break;
}
case NETSTRING_ERROR_NO_COLON:
{
MS_ERROR_STD("NETSTRING_ERROR_NO_COLON");
break;
}
case NETSTRING_ERROR_NO_COMMA:
{
MS_ERROR_STD("NETSTRING_ERROR_NO_COMMA");
break;
}
case NETSTRING_ERROR_LEADING_ZERO:
{
MS_ERROR_STD("NETSTRING_ERROR_LEADING_ZERO");
break;
}
case NETSTRING_ERROR_NO_LENGTH:
{
MS_ERROR_STD("NETSTRING_ERROR_NO_LENGTH");
break;
}
}
// Error, so reset and exit the parsing loop.
this->msgStart = 0;
this->bufferDataLen = 0;
return;
}
// If here it means that msgStart points to the beginning of a message
// with msgLen bytes length, so recalculate readLen.
readLen =
reinterpret_cast<const uint8_t*>(msgStart) - (this->buffer + this->msgStart) + msgLen + 1;
this->listener->OnConsumerSocketMessage(this, msgStart, msgLen);
// If there is no more space available in the buffer and that is because
// the latest parsed message filled it, then empty the full buffer.
if ((this->msgStart + readLen) == this->bufferSize)
{
this->msgStart = 0;
this->bufferDataLen = 0;
}
// If there is still space in the buffer, set the beginning of the next
// parsing to the next position after the parsed message.
else
{
this->msgStart += readLen;
}
// If there is more data in the buffer after the parsed message
// then parse again. Otherwise break here and wait for more data.
if (this->bufferDataLen > this->msgStart)
{
continue;
}
break;
}
}
10、OnConsumerSocketMessage
进入OnConsumerSocketMessage()这个函数
JS在传输之前会先把数据做成json的格式,然后以字符串的形式传输过来,C++收到字符串后,会把它转化为json对象
又调用Channel::Request,传入这个json对象
void UnixStreamSocket::OnConsumerSocketMessage(
ConsumerSocket* /*consumerSocket*/, char* msg, size_t msgLen)
{
MS_TRACE_STD();
try
{
json jsonMessage = json::parse(msg, msg + msgLen);
auto* request = new Channel::Request(this, jsonMessage);
// Notify the listener.
try
{
this->listener->OnChannelRequest(this, request);
}
catch (const MediaSoupTypeError& error)
{
request->TypeError(error.what());
}
catch (const MediaSoupError& error)
{
request->Error(error.what());
}
// Delete the Request.
delete request;
}
catch (const json::parse_error& error)
{
MS_ERROR_STD("JSON parsing error: %s", error.what());
}
catch (const MediaSoupError& error)
{
MS_ERROR_STD("discarding wrong Channel request");
}
}
11、Request
进入Channel::Request
可以看到这个json里面封装的是一个四元组,其包含:
- id——方法的id
- method——字符串的名字
- internal——自定义的内部格式
- data——传入的数据(可能有可能没有)
解析完数据之后,就把它们放入Request对象中的各个数据域 this->id、this->method、this->internal、this->data
Request::Request(Channel::UnixStreamSocket* channel, json& jsonRequest) : channel(channel)
{
MS_TRACE();
auto jsonIdIt = jsonRequest.find("id");
if (jsonIdIt == jsonRequest.end() || !Utils::Json::IsPositiveInteger(*jsonIdIt))
MS_THROW_ERROR("missing id");
this->id = jsonIdIt->get<uint32_t>();
auto jsonMethodIt = jsonRequest.find("method");
if (jsonMethodIt == jsonRequest.end() || !jsonMethodIt->is_string())
MS_THROW_ERROR("missing method");
this->method = jsonMethodIt->get<std::string>();
auto methodIdIt = Request::string2MethodId.find(this->method);
if (methodIdIt == Request::string2MethodId.end())
{
Error("unknown method");
MS_THROW_ERROR("unknown method '%s'", this->method.c_str());
}
this->methodId = methodIdIt->second;
auto jsonInternalIt = jsonRequest.find("internal");
if (jsonInternalIt != jsonRequest.end() && jsonInternalIt->is_object())
this->internal = *jsonInternalIt;
else
this->internal = json::object();
auto jsonDataIt = jsonRequest.find("data");
if (jsonDataIt != jsonRequest.end() && jsonDataIt->is_object())
this->data = *jsonDataIt;
else
this->data = json::object();
}
12、OnConsumerSocketMessage
完成上述步骤后,便又返回OnConsumerSocketMessage()
因为之前已经把数据转存到Request中,所以可以直接对其进行操作,这时候调用
this->listener->OnChannelRequest(this, request);
要注意的是,这里的listener实际上是Worker
13、OnChannelRequest
当Worker接收到Request的数据后,便能做出相应的处理了,接下来进入Worker.cpp的OnChannelRequest()
switch里就会根据methodId做出相应的处理,当它为
- WORKER_DUMP,表示将Worker中的Router信息都打印出来
- WORKER_GET_RESOURCE_USAGE,表示将RU的参数信息打印出来
- WORKER_UPDATE_SETTINGS,表示更新设置
- WORKER_CREATE_ROUTER,表示创建Router
- ROUTER_CLOSE,表示关闭
如果为其他的,就跳转到router相关的处理函数中,若router处理不了,就再往下传,一层一层传下去
inline void Worker::OnChannelRequest(Channel::UnixStreamSocket* /*channel*/, Channel::Request* request)
{
MS_TRACE();
MS_DEBUG_DEV(
"Channel request received [method:%s, id:%" PRIu32 "]", request->method.c_str(), request->id);
switch (request->methodId)
{
case Channel::Request::MethodId::WORKER_DUMP:
{
json data = json::object();
FillJson(data);
request->Accept(data);
break;
}
case Channel::Request::MethodId::WORKER_GET_RESOURCE_USAGE:
{
json data = json::object();
FillJsonResourceUsage(data);
request->Accept(data);
break;
}
case Channel::Request::MethodId::WORKER_UPDATE_SETTINGS:
{
Settings::HandleRequest(request);
break;
}
case Channel::Request::MethodId::WORKER_CREATE_ROUTER:
{
std::string routerId;
// This may throw.
SetNewRouterIdFromInternal(request->internal, routerId);
auto* router = new RTC::Router(routerId);
this->mapRouters[routerId] = router;
MS_DEBUG_DEV("Router created [routerId:%s]", routerId.c_str());
request->Accept();
break;
}
case Channel::Request::MethodId::ROUTER_CLOSE:
{
// This may throw.
RTC::Router* router = GetRouterFromInternal(request->internal);
// Remove it from the map and delete it.
this->mapRouters.erase(router->id);
delete router;
MS_DEBUG_DEV("Router closed [id:%s]", router->id.c_str());
request->Accept();
break;
}
// Any other request must be delivered to the corresponding Router.
default:
{
// This may throw.
RTC::Router* router = GetRouterFromInternal(request->internal);
router->HandleRequest(request);
break;
}
}
}