webrtc-new-发送端-probe.drawio.pnggithub:https://github.com/bigonelby/webrtcUml/tree/master/latest
-
这张图介绍了probe的过程
-
probe过程比较复杂,由于是对网络的探测,因此大致的思路,就是构建probecluster;将probecluster信息分配给发送的数据包;将发送信息缓存在history中;收到feedback的时候,从缓存中找到相应的发送信息;由发送,接收的时间推算出bitrate
-
上面介绍了大概过程,对于cluster而言,还经历了合-分-合这几个阶段,刚刚创建cluster的时候,每个cluster都是一个整体,是一个合的状态;当需要将cluster分配给发送包时,有可能很多个包对应相同的cluster,这就相当于将分配好的cluster分解,开来了;最后收到feedback时,要将这些分解后的cluster重新合并,这又是一个合的过程
-
首先要看看的就是cluster的产生,这个cluster的产生实际上幕后的推手就是RtpTransportControllerSend,这是一个至关重要的类,他的很多行为都会导致cluster的产生,比如当网络变化时;当网络路由变化时;当设置配置修改时等,都会触发重新probe。RtpTransportControllerSend的得力助手自然就是NetworkControllerInterface了,而其实现类GoogCcNetworkController也是我们的老朋友了。我们以周期性的probe为例,来看看probe产生的过程。周期性的触发者就是RtpTransportControllerSend,在StartProcessPeriodTasks时,开启周期性更新,即UpdateControllerWithTimeInterval,进而触发NetworkControllerInterface的OnProcessInterval方法,然后调用ProbeController的Process方法。ProbeController,顾名思义,就是Probe的控制器,因此会根据当前的网络带宽预估值estimated_bitrate_bps_,结合ProbeControllerConfig配置,决策出需要构建的ProbeClusterConfig的target_data_rate,以及id这些初始的关键信息。可以说ProbeController为probecluster分配了唯一的clusterid,以及对应的target_data_rate,这是cluster的起点
-
继续看GooCCNetworkController的OnProcessInterval方法,这个方法返回了NetworkControllerUpdate,这个类其中的成员就是probe_cluster_configs,这个vector是由ProbeController产生的。至此,RtpTransportControllerSend得到了助手GooCCNetworkController的决策报告,即NetworkControllerUpdate,在这个报告里,有ProbeClusterConfig信息,这个信息将送给下一个部门
-
下面我们看第二部分,pacing模块根据指示构建ProbeCluster。上面的介绍已经知道,RtpTransportControllerSend已经拿到助手的决策报告,因此已经知道需要进行probe,以及所对应的ProbeClusterConfig,紧接着,RtpTransportControllerSend就把这份报告交给了pacing模块,其pacer()指向的正是PacedSender,这个模块负责将rtp包发送到网络,不过在此之前,他要首先看看probe的情况。当他发现需要probe时,就将相关的信息,交给他的助手prober_了,即BitrateProber,BitrateProber的作用就是根据决策出来的ProbeClusterConfig这个配置,生产出ProbeCluster了。ProbeCluster有个关键的信息就是pace_info,即PacedPacketInfo,这可是一个核心的信息!首先,send_bitrate_bps指明了这个cluster的目标探测比特率,probe_cluster_id正是每个cluster的唯一标识符,起到身份证的作用!接下来probe_cluster_min_probes和probe_cluster_min_bytes,这两个成员也很重要,他们是从BitrateProberConfig获取到的,其作用是决策出这个cluster究竟需要多少次probe,以及最小的bytes,换句话说,这两个变量决定了这个cluster到哪里才算是完成了任务!
-
我们来看看究竟可以从BitrateProber中获取什么,最重要的就是刚开始知道要进行probe时,通过CreateProbeCluster生产出所有的ProbeCluster,保存在其成员clusters_中,创建的过程上面已经说了,根据传递下来的ProbeClusterConfig。然后通过ProbeSent这个方法,告诉BitrateProber模块究竟已经probe发送了多少数据了,这里还有一道手续,就是用到了6中介绍的两个变量,根据probe_cluster_min_probes和probe_cluster_min_bytes这两个阈值,决定了这个cluster是否已经完成任务了,如果超过这两个阈值,则会将这个cluster从vector中移除。最后一个核心的方法就是CurrentCluster了,他会返回当前的ProbeCluster的PacedPacketInfo
-
一切顺利的话,就就到达第三部分了,即ProbeCluster印记送达发送模块。什么是ProbeCluster的印记呢,就是PacedPacketInfo!Pacing的过程之前已经介绍过,PacingController会通过PacketRouter的SendPacket将包发送出去,当然,这里要补充两个额外的工作,就是要拿到rtppacket所分配的cluster,即通过BitrateProber的CurrentCluster方法,得到当前的PacedPacketInfo印记,这个印记将随着这个rtp包一直传输到发送层。当成功发送数据后,要通过BitrateProber的ProbeSent方法来更新probe的数据,内部会决策当前的probecluster是否已经达到阈值,完成任务,如果确实已经达到阈值,则会从clusters_这个向量中移除出去,这样,下一个包就将用之后的印记了。通过这个过程,我们也可以了解到,这就是上述的从合到分的过程,一个cluster可以对应多个rtp包,每当发送rtp包的时候,如果处于probe状态,都会从BitrateProber中拿到对应的印记PacedPacketInfo,如果处理的几个包没有超过两个cluster阈值,则这些包使用的是相同的cluster,即他们的clusterid都相同。这样,就将同一个cluster分配到不同的rtp包中去了,完成了从合到分的过程
-
pacing的发送过程之前已有介绍,这里不再复述,总之,最终rtp包到达了RtpSenderEgress模块,随着rtp包一同而来的,就是其印记PacedPacketInfo。RtpSenderEgress会构建RtpPacketSendInfo这个类,这个类的成员pacing_info正是这个包的印记PacedPacketInfo。构建好RtpPacketSendInfo后,RtpSenderEgress将这个信息反馈给其观察者TransportFeedbackObserver,这个观察者有一个接口就是OnAddPacket,就是专门为了收集RtpPacketSendInfo信息的
-
接下来终于来到了第四部分发送模块缓存cluster印记。前面已经介绍,发送前,每个包对应的印记将以回调的形式汇报给TransportFeedbackObserver,而实现这个接口的正是RtpTransportControllerSend!可见RtpTransPortControllerSend就如同如来佛祖一样,掌控着所有信息。接着,RtpTransportControllerSend将此信息转达给TransportFeedbackAdapter,他会将这些核心信息保存在其history_中,每个history_的成员都是PacketFeedback,这个类信息是由RtpPacketSendInfo生成的。最终,包的印记,就被保存在PacketFeedback的SentPacket的pacing_info成员中,供后续使用
-
终于一路来到了第五部分,即发送模块收到feedback后,根据缓存决策最终bitrate。前面已经说过每个包在发送之前,都会将pacing印记保存在TransportFeedbackAdapter中。接下来我们介绍一下这些保存的pacing印记究竟该如何使用。真正的使用的地方,就是在接收到这些包的feedback反馈时,此时该包已经发送到接收端,接收端整理了收到这些包的信息,通过transport-feedback的RTCP报文,反馈给发送端。关于transport-feedback报文,之前已经介绍过,这里不再重复。而接收这些报文信息的,依然是我们的老朋友,佛祖,RtpTransportControllerSend。通过其OnTransportFeedback方法得到这些信息。当然,这些信息还是需要交由TransportFeedbackAdapter处理一下,因为通过transport-feedback报文,我们只能知道每个包的接收信息,那么发送信息该如何获取呢?当然是TransportFeedbackAdapter的缓存history_中。这个history_是一个map,其key即为transport-id,这个id是每个包独一无二的。通过这个transport-id的索引,我们可以在history_中找到对应的PacketFeedback,其send成员SentPacket记录了发送的相关信息,当然,当初的pacing印记PacedPacketInfo也在这里
-
经过TransportFeedbackAdapter的加工后,将信息汇总到TransportPacketsFeedback中,这个类由TransportFeedbackAdapter生成,其维护的向量表packet_feedbacks记录了包信息,对应的类为PacketsResult。特别的PacketsResult的sent_packet成员即为SendPacket,记录着包的发送相关信息
-
最终RtpTransportControllerSend会将处理好的信息TransportPacketsFeedback交给controller_,即GoogCcNetworkController,该模块会进一步将信息交给其成员probe_bitrate_estimator_来处理。这个成员对应的类是ProbeBitrateEstimator,这个类接收所有有probe印记的包,并进一步处理。每个PacketsResult信息,都会进行HandleProbeAndEstimateBitrate的处理。这个处理方式将所有相同clusterid的包,合并成相同额AggregatedCluster,这里体现了从分到合的过程。整理成AggregatedCluster后,通过信息就可以决策出发送的码率和接收的码率了,因为信息已经很充分,我们知道这个cluster的实际大小,以及发送和接收的相关信息了。当发送码率和接收码率比较接近时,我们选取两者的最小值,作为评估后的带宽值,即estimated_data_rate_。至此,整个probe过程终于可以告一段落!
网友评论