你们都知道奥斯卡有提名,其实在 WebRTC 的 ICE 中也有提名,有常规的提名,也有激进的提名,并且提名的候选人不必定是最优秀的候选人喔,本文就带你一探其中玄妙。文章内容主要描述 RFC 5245 中 ICE 相关的状态和 ICE 提名机制,并结合 libnice(0.14) 版本进行分析。html
做者:阵图,阿里云开发工程师
审校:泰一,阿里云高级开发工程师网络
分析一个问题时候遇到这样的场景:服务端一个 Candidate,客户端三个不一样优先级的 Candidate,可是最后竟然选择了一个优先级最低的 Pair。并发
服务端有一个 Relay Candidate,端口 50217。ide
a=candidate:3 1 udp 503316991 11.135.171.187 50217 typ relay raddr 10.101.107.25 rport 40821flex
客户端有三个 Candidate,端口 50218(中间优先级),50219(最低优先级),50220(最高优先级)。阿里云
Candidate 1: candidate:592388294 1 udp 47563391 11.135.171.187 50219 typ relay raddr 0.0.0.0 rport 0 generation 0 ufrag fO75 network-cost 50 Candidate 2: candidate:592388294 1 udp 48562623 11.135.171.187 50218 typ relay raddr 0.0.0.0 rport 0 generation 0 ufrag fO75 network-cost 50 Candidate 3: candidate:592388294 1 udp 49562879 11.135.171.187 50220 typ relay raddr 0.0.0.0 rport 0 generation 0 ufrag fO75 network-cost 50
可是最后选择的倒是最低优先级的 Pair,50219。code
Remote selected pair: 1:1 592388294 UDP 11.135.171.187:50219 RELAYED
视频
Candidate 的 Foundation: 这里先提一下 Foundation,会涉及到 Frozen 状态。htm
对于一条相同的信道,可能有不一样的 Candidate,好比 Relay Candidate 被发现的时候,就能够生成一个新的 Server Reflexive 类型的 Candidate,可是他们都是基于相同的本地地址(IP,端口)和协议,则能够认为这些网络是类似的,则他们就会有相同的 Foundation。其中 Foundation 在 SDP 中为第一个字段,即下面例子中的 '7'。blog
a=candidate:7 1 udp 503316991 11.178.68.36 51571 typ relay raddr 30.40.198.7 rport 55896
ICE 主要有如下五种状态,其中前四种是正常的状态,第五种状态 Frozen 涉及到 ICE Frozen Algorithm。
ICE 的五种状态:
Frozen: ,全部 Candidate Pair 初始化完成之后就在这个状态,对于相同的 Foundation(类似的 Candidate),会按照优先级依次选取一个 Pair,Unfreeze,并设置为 Waiting 状态,其余则保持 Frozen。直到选取的 Pair 完成,才会继续 Unfreeze 另外一个 Pair。
ICE 有两种提名方式:
对于常规提名,主要工做流程以下:
L R - - STUN request -> \ L's <- STUN response / check <- STUN request \ R's STUN response -> / check STUN request + flag -> \ L's <- STUN response / check Regular Nomination
Controlling 模式下的 Agent 发起 Binding Request,而且收到对端的 Response,同时对端发起的 Connective Check 完成,Controling 一端会再次发出一个携带 USE_CANDIDATE 标志位的 Binding Request,当 Controlled 一端收到了,就接受此次提名。
除了常规提名,还有一种比较激进的提名,常规提名中会新增一次握手。
L R - - STUN request + flag -> \ L's <- STUN response / check <- STUN request \ R's STUN response -> / check Figure 5: Aggressive Nomination
Controlling 模式下的 Agent 发起 Binding Request,可是在这个 Binding Request 中会直接携带 USE_CANDIDATE 的标志位,Controlled 模式下的 Agent 收到了之后就接受此次提名。在激进提名模式下,能节约一次握手过程,可是当多个 Pair 同时接受提名时,会根据这些 Pair 各自的优先级进行选择,选择出优先级最高的 Pair 做为实际的信道。
真实案例:
当一个新的提名产生时,会对 ICE 内部状态进行对应的变化。
当一端的 Binding Request 携带了 Use Candidate 的标志位时,则会产生一次提名(Nomination)。
无论 Controlling 或者 Controlled 模式下的 Agent,处理提名的状态更新规则建议以下:
当某一个 Stream 的全部 Compont 都至少拥有一个提名时,且检查仍然在进行时:
当检查列表中的全部 Pair 都完成时:
当检查列表检查有失败时:
在描述提名时,还会涉及 ICE 对 Pair 的调度(当有效 Candidate 还在 In Progress 的时候可是其余 Candidate 的 Pair 已经收到 Binding Request)。
这里只讨论 Full,先不描述 Lite 模式。
ICE 的 Checks 分红两种,Ordinary Checks And Triggered Checks。
当 ICE 创建一个 Check List (每一个 Stream 一个)后,会对每一个 Check List 添加一个定时器,当定时器到来时,会进行以下调度:
注:这里有点不能理解,整个流程看起来是串行的,激活速度有点慢。
若无,终止调度。
简单了解了 ICE 的流程后,咱们回归最开始的 Case。
首先看 Add Candidate,三个 Remote Candidate 添加顺序不一样,依次为 50219,50218,50220,注意,此时 50219 收到了对端的 Binding Request,激进提名,携带 USE_CANDIDATE,所以很快执行 Create Permission 并完成,这时候能够开始发送 Binding Request 了,属于 Triggered Checks 优先调度,发送 Binding Request,并进入 In Progress 状态。
注:这里除了本地 Relay 的 Pair,还有和 Turn 通讯的本地 Host 类型的 Candidate。
接着在 50219 在其余两个 Create Permission 还没完成时候以迅雷不及掩耳之势完成了 Check,根据 rfc 8.1.2 中的描述,对于不是在 In Progress 状态的 Pair,都删除,并不参考其优先级,故最后选择了 50219 这个优先级最低的 Pair。
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。