Worker:mediasoup 流媒体处理进程
Router:路由对象,类似于房间的功能,保存了流之间的订阅关系,它接收 Producer 的数据并转发给订阅该 Producer 的 Consumer
Producer:音视频流生产者,一路音频就算一个单独的生产者,一路视频也算一个单独的生产者
Consumer:音视频流消费者
Transport:传输通道,有 WebRtcTransport,PlainRtpTransport 以及 PipeTransport
上图解释如下:一个 Worker 可以有多个 Router,客户端通过 Transport 与后台进程产生连接,并通过 Transport 发送流数据或者消费流数据。通道有多种类型,有基于 WebRTC 协议的通道,也有普通的 RTP/RTCP 通道,还有不同 Router 间转发数据的 PipeTransport。
Websocket 监听连接请求并创建媒体层的路由对象。
通过 Websocket 信令创建房间路由后,客户端与媒体需要建立通道,客户端可以通过通道把媒体流发送给服务端(生产),也可以通过通道消费其他流的数据(消费)。Transport 有 PlainTransport,WebRtcTransport 等,我们以 WebRtcTransport 为例。
WebRtcTransport 的构造函数中通过 listenIp, port 创建 UdpSocket,UdpSocket 继承自 UdpSocketHandler,UdpSocketHandler 包含了 uv_udp_t* 对象,它是一个 libuv 的 UDP 事件处理对象,我们通过注册相应的回调函数即可在有事件发生时调用回调函数处理。
在服务端创建 WebRtcTransport 对象后,还需要通过 connectWebRtcTransport 才能让客户端与该通道对象产生关联,该流程主要是进行 DTLS 认证。
创建连接通道后,客户端可以通过该通道发送流媒体数据,每一路流称为一个 Producer。
调用 produce 后,Transport 会回调 Router::OnTransportNewProducer 告知 Router 该路由对象产生了一个新的生产者,Router 会记录 producer_id 与 Producer 对象的映射关系,相当于保存了该路由对象当前有哪些生产者。
Router 有了流数据的生产者,还需要有消费该 Producer 的 Consumer,这样 Router 才会将收到的 Producer 数据转发给 Consumer。
在这篇博客中介绍了信令的交互流程,通过 join 加入房间时,已经为准备加入房间的用户订阅了所有存在于房间的 Producers。
C++ Transport 收到 TRANSPORT_CONSUME 请求后,根据 type 创建出相应类型的 Consumer,然后通知 Router::OnTransportNewConsumer,在该函数中最重要的就是存放 Producer 与 Consumers 的映射关系,该结构体对象为:std::unordered_map<RTC::Producer*, std::unordered_set<RTC::Consumer*>> mapProducerConsumers,即将新的 Consumer 加入到Producer 对应的 Consumer 集合中。
当生产者发送媒体数据后,首先得到通知的是 UDP socket,因为我们使用 libuv 注册了读事件的回调,因此会回调 onRecv,再调用 UdpSocketHandler::OnUvRecv,之后再调用 WebRtcTransport::OnUdpSocketPacketReceived,之后的调用流程如下所示。
最终可以看到 WebRtcTransport::OnRtpDataReceived 处理完再交给父类 RTC::Transport::ReceiveRtpPacket,其根据 Packet 的 ssrc 确认 Producer 源对象后,调用 Produer 的 ReceiveRtpPacket 处理,之后再通知 Transport::OnProducerRtpPacketReceived 处理,Transport 再通知 Router::OnTransportProducerRtpPacketReceived 处理,Router 根据保存的订阅关系,调用 Consumer::SendRtpPacket 将数据包给相应通道的客户端。