mediasoup中的rtp数据流向大概为:
推流客户端–》router–》producer–》consumer–》发送到观看客户端
这里就涉及问题,如何在router中将producer与consumer关联在一起,使得当producer收到流的时候能够推给每个consumer
mediasoup中包含c++部门和客户端代码,其中c++部分里在router中记录了所有的producer和对应的consumer,类结构为:
class Router : public RTC::Transport::Listener
{
std::unordered_map<RTC::Producer*, std::unordered_set<RTC::Consumer*>> mapProducerConsumers;
std::unordered_map<RTC::Consumer*, RTC::Producer*> mapConsumerProducer;
}
那么这个结构中的producer和cunsumer即调用生成(这两个接口需要应用层发送TRANSPORT_PRODUCE和TRANSPORT_CONSUME来调用到,当然应用层会经过一系列交互之后如ICE,SDP等才会调用到这两个命令):
inline void Router::OnTransportNewProducer(RTC::Transport* /*transport*/, RTC::Producer* producer)
inline void Router::OnTransportNewConsumer(
RTC::Transport* /*transport*/, RTC::Consumer* consumer, std::string& producerId)
这里可以看到,每个consumer都会和一个producerId进行绑定,从而在后续收到rtp流的时候可以找到这种对应关系来进行流的转发。接下来考虑的问题,应用层如何获取到这个produceId和对应的consumer类型?
查看创建producer时的代码可以发现,当通过进行见通讯应用层将数据传递到transport时handlerequest里处理了TRANSPORT_PRODUCE并生成了producerId和consumer类型传递给了应用层:
case Channel::Request::MethodId::TRANSPORT_PRODUCE:
{
std::string producerId;
// This may throw.
SetNewProducerIdFromInternal(request->internal, producerId);
// This may throw.
auto* producer = new RTC::Producer(producerId, this, request->data);
到这里,所有的consumer和producer就能关联到一起了,剩下的就是接收码流时的操作,数据依旧是从应用层传来,PRODUCER_SEND,此时检测到rtp包,接收到之后调用
RTC::Transport::ReceiveRtpPacket(packet);
判断ssrc,如果是新的码流那么会通知consumer增加一条流的接受,
NotifyNewRtpStream(rtpStream);
如果是已有的流的话那么就直接传递至consumer并发送出去
inline void Router::OnTransportProducerRtpPacketReceived(
RTC::Transport* /*transport*/, RTC::Producer* producer, RTC::RtpPacket* packet)
{
MS_TRACE();
auto& consumers = this->mapProducerConsumers.at(producer);
for (auto* consumer : consumers)
{
// Update MID RTP extension value.
const auto& mid = consumer->GetRtpParameters().mid;
if (!mid.empty())
packet->UpdateMid(mid);
consumer->SendRtpPacket(packet);
}
}
通过以上描述基本就完成了整个视频数据链路的传递,每个consumer都可以接收到码流了