目前基于WebRTC的低延时直播很火热,相比较WebRTC对客户端做的标准,服务端的信令媒体并没有严格规范,现在主流的媒体服务器有Licode、Janus、MediaSoup等。Mediasoup作为一个高性能的SFU媒体服务器,代码结构清晰,上手容易,可定制化较强。我接触MediaSoup有挺长一段时间,基于MediaSoup v3.0做过一定的开发优化,计划写个专题,大家可以参考一下。
1. 基本概念
网上关于Mediasoup基础概念的介绍一大堆,这里不再展开写了,给个链接。
https://blog.csdn.net/Dreamandpassion/article/details/107799915
2. MediaSoup框架介绍
原生的MediaSoup分为JS和C++两部分,JS部分属于MediaSoup业务部分,可以根据项目需要进行修改,我对JS理解有限,项目中剔除掉JS部分代码,只保留C++核心代码,根据项目需求用C++重写了JS的部分功能。所以这个专题主要介绍MediaSoup C++部分相关处理。
MediaSoup使用多进程模型,有一个主应用进程(JS),主要做媒体流的状态管理、Worker进程的管理(包括Worker进程的创建、通信、销毁等)等,Worker进程创建的个数依赖于服务器的CPU核数,有多个少CPU核,就启动多少个Worker进程;Worker进程做流的处理,包括RTP包的收发、带宽评估、丢包重传等,这部分是MediaSoup的核心功能。
这里主要分析下Worker进程收发数据的过程,包括两类:一类是收发应用进程的数据;另一类是收发网络数据。
2.1 收发应用进程数据
2.1.1 初始化过程
应用进程与Worker进程通过SocketPair管道进行通信。每个Worker进程都是基于libuv来做的,是单线程的事件触发机制。当有数据到达时,就会触发libuv的事件,调用预先注册好的回调函数进行处理。初始化流程如下:
1.在Worker进程的main函数中(mediasoup/worker/src/main.cpp),创建Channel::UnixStreamSocket对象,它是libuv对管道的封装。
int main(int argc, char* argv[])
{
// Ensure we are called by our Node library.
if (!std::getenv("MEDIASOUP_VERSION"))
{
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);
}
// ...
}
2.初始化consumerSocket对象和producerSocket对象分别用于与应用进程的数据收发,注意它们的基类是::UnixStreamSocket,它位于全局空间内,与上述Channel::UnixStreamSocket对象是两个对象。
UnixStreamSocket::UnixStreamSocket(int consumerFd, int producerFd)
: consumerSocket(consumerFd, NsMessageMaxLen, this), producerSocket(producerFd, NsMessageMaxLen)
{
MS_TRACE_STD();
}
// 只列出consumerSocket初始化,producerSocket初始化类似
ConsumerSocket::ConsumerSocket(int fd, size_t bufferSize, Listener* listener)
: ::UnixStreamSocket(fd, bufferSize, ::UnixStreamSocket::Role::CONSUMER), listener(listener)
{
MS_TRACE_STD();
}
UnixStreamSocket::UnixStreamSocket(uv_pipe_t *uvHandle, size_t bufferSize, UnixStreamSocket::Role role)
: bufferSize(bufferSize), role(role)
{
MS_TRACE_STD();
this->uvHandle = uvHandle;
int err;
// this->uvHandle = new uv_pipe_t;
uvHandle->data = static_cast<void*>(this);
if (this->role == UnixStreamSocket::Role::CONSUMER)
{
// Start reading.
err = uv_read_start(
reinterpret_cast<uv_stream_t*>(uvHandle),
static_cast<uv_alloc_cb>(onAlloc)