Catalog
  1. 1. uml
  2. 2. WebRtcConnection
  3. 3. MediaStream
  4. 4. NicerConnection
  5. 5. Transport
  6. 6. OneToManyProcessor
  7. 7. Pipeline
erizo(c++)关键类分析-Licode系列NO.7

licode 的 erizo c++ 库配合 nICEr 库实现了 sfu 服务器媒体管理方面的功能。包括通道的建立 通道的管理,推拉流。这里大体分析几个关键的类,说明他们的职能和关系。这样能能快速的了解大体架构。

uml

TIM截图20200109121745.png

WebRtcConnection

rtc 连接对象,作为一个管家的角色,管理对应的 MediaStream 和 Transport。同时也处理 Transport 传递过来的 ICE 请求与返回,并转发 rtp/rtcp 数据给MediaStream。

MediaStream

WebRtcConnection收到 rtp/rtcp 数据后,交由 MediaStrem 处理。其内部维护一个 Pipeline,加入了非常多的处理模块,对 rtp/rtcp 做处理, 下面大致分析了一下, 当收到或者发送 rtp/rtcp 时,会做哪些事情。后面打算开更加细致的文章,对这些处理模块的原理和作用做更深入的讨论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
## 接收数据
/*
解析编解码器信息。 如果是 rtp ,根据 payloadtype (具体映射关系还没找到) 从当前流的 SDP 中提取出编解码器信息和采样率
*/
PacketCodecParser
/*
探测视频包,根据不同编解码获得关键帧信息等。只处理视频包。
*/
LayerDetectorHandler
/*
发送端带宽控制, 根据收到的 RTCP 报告,动态调整发送带宽
*/
SenderBandwidthEstimationHandler
/*
直接流转到下一级
*/
SRPacketHandler
/*
只处理 RTCP_RTP_Feedback_PT (RTCP Transport Layer Feedback Packet) 包。丢包重传。需要配合 rtcp 协议一起看,可以参考 WebRTC系列的 rtp/rtcp 协议。
*/
RtpRetransmissionHandler
/*
如果是 rtp 包,根据实际情况创建 nack(丢包重传机制,参考 webrtc QOS) 报告和 receiver 报告。 如果是发送者报告,则处理报告后继续流水线。
音频包默认不开启 nack 机制, 视频包默认开启。
*/
RtcpFeedbackGenerationHandler
/*
当是视频包时才处理, 清除 padding 字段的内容。还不清楚意义在哪里。
*/
RtpPaddingRemovalHandler
/*
接收端带宽估计,收到 rtp 包,估计(复杂的算法)带宽,生成 rtcp 报告提醒发端调整发送带宽。
当是视频包时才处理。
*/
BandwidthEstimationHandler
/*
视频关键帧处理
*/
PliPacerHandler
/*
不做处理,流转到下一步
*/
RtpPaddingGeneratorHandler
/*
*/
RtpSlideShowHandler
/*
静音处理
*/
RtpTrackMuteHandler
/*
不做处理,流传到下一步
*/
FakeKeyframeGeneratorHandler
/*
根据包类型,生成一些事件,通知给观察者
*/
IncomingStatsHandler
/*
如果是 rtcp 包,根据请求发送关键帧。
*/
QualityFilterHandler
/*
处理发送者报告
*/
RtcpProcessorHandler
/*
转发给订阅者
*/
PacketReader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
## 发送数据
/*
如果是 feedback 包, 执行 RtcpForwarder::analyzeFeedback。设置信源,打印一些日志。
*/
RtcpProcessorHandler
/*
视频包才做处理(可能是音频用了 opus 编码器,opus 内置了 fec)
*/
FecReceiverHandler
LayerBitrateCalculationHandler
/*
视频包才做处理
*/
QualityFilterHandler
/*
视频包才做处理
*/
FakeKeyframeGeneratorHandler
/*
如果是 rtcp 包则不处理
如果 mute 了, 则返回,后面也不会再执行了
*/
RtpTrackMuteHandler
/*
视频包才做处理
*/
RtpSlideShowHandler
/*
视频包才做处理
*/
RtpPaddingGeneratorHandler
/*
视频关键帧才做处理
WebRTC引入pacer,pacer会根据estimator评估出来的码率,按照最小单位时间(5ms)做时间分片进行递进发送数据,避免瞬时对网络的冲击。pacer的目的就是让视频数据按照评估码率均匀的分布在各个时间片里发送, 所以在弱网的WiFi环境,pacer是个非常重要的关键步骤。
*/
PliPacerHandler
/*
不处理
*/
BandwidthEstimationHandler
/*
视频帧的 feedback 包才处理
*/
RtpPaddingRemovalHandler
/*
不错处理
*/
RtcpFeedbackGenerationHandler
/*
rtp 包插入缓存队列, 队列长度 256
*/
RtpRetransmissionHandler
/*
更新发送者信息,比如已发送包数、字节数。
如果是发送者报告包,则填充已发送字节数和包数信息到 rtcp
*/
SRPacketHandler
/*
视频包才做处理
*/
SenderBandwidthEstimationHandler
/*
生成一些通知事件
*/
OutgoingStatsHandler
/*
发送数据包
*/
PacketWriter

NicerConnection

作为 sfu 业务逻辑与 nICEr 库的胶合层。这个类会持有一个 IOWorker(独立线程) 来调用 nICEr 接口以集成 ICE 能力以及 rtp/rtcp 数据收发能力。每个 rtc 通道持有该类对象,start() 接口来启动 ICE 流程,其他主要的接口用来接收 nICEr 通知或者数据事件。

Transport

它相当于是一个传输层,它不关心数据的内容,他只负责对数据预处理然后转发。这个类抽象了一些接口,用于处理 ICE 的数据和 rtp/rtcp 数据,具体的逻辑需要继承这个类来实现。实际处理数据的对象是 DTlsTransport, 基于 dtls 实现的 srtp/srtcp, 对数据加解密后交付给上层或者传递给下层。

1
2
// 这个 linstener 是一个 WebRtcConnection 对象。
listener->onTransportData(...);

收到数据后,将处理后的数据包传递给 WebRtcConnection 处理。

OneToManyProcessor

licode 中有 publisher 和 Subscriber 的概念, 每个 Publisher 享有一个 OneToManyProcessor。这个类为 Publisher 维护了 Subscriber 列表。Publisher 推流数据由这个类分发给所有的 Subscriber。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 媒体转发
int OneToManyProcessor::deliverAudioData_(std::shared_ptr<DataPacket> audio_packet) {
if (audio_packet->length <= 0)
return 0;

boost::unique_lock<boost::mutex> lock(monitor_mutex_);
if (subscribers.empty())
return 0;

std::map<std::string, std::shared_ptr<MediaSink>>::iterator it;
RtpHeader* head = reinterpret_cast<RtpHeader*>(audio_packet->data);
RtcpHeader* chead = reinterpret_cast<RtcpHeader*>(audio_packet->data);
for (it = subscribers.begin(); it != subscribers.end(); ++it) {
if ((*it).second != nullptr) {
if (chead->isRtcp()) {
chead->setSSRC((*it).second->getAudioSinkSSRC());
} else {
head->setSSRC((*it).second->getAudioSinkSSRC());
}
// Note: deliverAudioData must copy the packet inmediately
(*it).second->deliverAudioData(audio_packet);
}
}

return 0;
}

可以看到,媒体转发,就是遍历 Subscriber,调用他们的发送接口。

Pipeline

顾名思义, 这是一个流水线,按照创建管道时设定的规则,依次处理数据。MediaStream 就持有一个流水线,收发的 rtc/rtcp 会经过流水线做非常多的预处理。

Author: 42
Link: http://blog.ikernel.cn/2020/01/07/erizo(c++)%E5%85%B3%E9%94%AE%E7%B1%BB/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment