WebRTC是一种通过由万维网联盟(W3C)起草的为浏览器和移动应用提供实时通信(RTC)功能的技术简。 在本指南中,我们将向您介绍重要的部分,以及它们如何适用于Kurento。
Kurento WebRTC旨在成为WebRTC通信的一方。 另一个对等体可以是具有WebRTC能力的浏览器 - 使用RTCPeerConnection API,本地WebRTC移动应用程序或甚至另一个Kurento媒体服务器。 以下部分介绍了如何建立此连接,以及在此过程中可以找到的挑战。
连接,防火墙和NAT穿越
WebRTC允许在两个对等体之间交换数据,音频或视频,或其任何组合。 会话的生命期周期是从建立连接到由对等体中的任一个结束会话。 在本节中,我们将学习如何在两个对等体之间建立连接,这是对等体Kurento Media Server之一。
准备媒体会话
WebRTC中的会话建立需要在对等体之间交换某些信息,主要是IP寻址,端口位置和两个对等体的媒体能力。这就是所谓的信令。了解信令流程的最重要的事情是它不包括在常规的局域网络中,而是在复杂的网络环境中。例如我们所处的环境中,通过不同的路径连接,两个设备无法直接联系,所以没有办法预见到WebRTC的所有可能的用例。
信令的目的是在对等体之间传送某些消息,并确保它们以正确的顺序到达。这些是SDP和ICE候选人。这可以通过XMPP,SIP或您自己的协议来完成。
对,它是如何工作的?让我们考虑最简单的情况,想象从浏览器发送媒体到Kurento媒体服务器。这一切都从会话描述消息的交换开始。这些消息会告诉其他对等方我们对媒体会话的能力:正在发送的媒体的类型,格式,传输协议,端点的IP地址,端口,以及通常描述媒体端点所需的任何信息。该信息使用会话描述协议(SDP)来交换和存储;描述在RFC 2327。
当一个对等体想要与另一个对等体连接时,它需要创建称为Offer的特殊会话描述消息。该描述包括关于呼叫者建议的会话配置的所有信息。接收者然后用Answer来回答,回答是对他们会话结束的描述。以这种方式,两个设备彼此共享描述媒体交换会话的信息。使用交互式连接建立(ICE)来处理该交换,该协议允许两个设备使用中间设备来交换提议和应答,即使两个设备由网络地址转换(NAT)分离。
媒体服务器端点可以充当呼叫者或接收者。 让我们使用Kurento Media Server查看这两种情况。
Kurento 作为提议者 (offerer)
为了使WebRtcEndpoint初始化连接,端点必须生成SDP提议。
String offer = webRtcEndpoint.generateOffer();
connection.sendToPeer(offer);
一旦生成offer,它必须发送到远程对等体进行处理。 这将产生该提议的Answer,并且该Answer将通过信令信道发送回Kurento。 Kurento的对等体(提议者)必须处理收到的Answer。
webRtcEndpoint.processAnswer(remoteAnswer);
这将结束两个对等体之间的协商。
远程对等体作为提议者 (offerer)
如果Kurento从远程对等体接收到SDP提议,则我们必须使用WebRtcEndpoint处理此提议,并生成相应的Answer。
String answer = webRtcEndpoint.processOffer(remoteOffer);
connection.sendToPeer(answer);
该Answer必须通过信令信道返回给远程对等体,以完成协商过程。
现在让我们看一下协商媒体会话后如何建立连接。
ICE ICE Baby!
如果事情很简单,每个对等体都将有一个唯一的地址,它可以与其他对等体交换以便进行通信。 实际上,由于不同的原因,大多数设备位于可变数量的NAT层之后。 它是安全的控制对某些资源的访问,或最常见地由于IPv4地址空间不足。 在其他情况下,对等体具有阻止端口和协议的防火墙。 在最坏的情况下,客户端在防火墙NAT之后。
WebRTC客户端存在于什么样的环境中没有明确的概念。因此,必须要有一些机制让对等体发现它在哪里。 使用STUN(用于NAT的会话穿越实用程序)和TURN(使用中继穿越NAT)服务器。
STUN是一种允许对等体发现网络中存在NAT的协议,并且在这种情况下获得公共IP。 STUN服务器必须有一个的公共IP。 客户端发送绑定请求,服务器回应从公共网络看到的客户端的公共IP和端口。 这样,客户端可以发现其公共IP和端口。 此外,对于大多数NAT实现,它将建立NAT路由条目,以便对该IP和端口的请求通过内部网络找到他们的方式。
不幸的是,这并不适用于所有场景。 在对称NAT实现的情况下,到STUN服务器的绑定请求打不开远程对等体到达NAT之后的反向路径对等体。 在其他情况下,UDP可能一直被阻止,这时需要使用TCP - 在许多公司网络中非常常见的情况。 为了在STUN不起作用的那些不利环境中也能够建立信道,对等体可以使用TURN协议,其可以在UDP上运行并且如果所有其它都失败则切换到TCP。
TURN中最重要的概念是“中继”。 该协议使用可公开访问的服务器,在对等体之间路由数据。
显然,不求助于TURN服务器是合乎需要的,因为它在两个对等体之间增加一个了间接,并且会因此有延迟。 然而,这在所有情况下都不可能,因此每个构建的WebRTC解决方案都必须准备好支持两种类型的服务。 Kurento可以配置为单独使用STUN和TURN服务器。 请参考STUN和TURN配置指南。
现在我们已经看到了连接过程中涉及的所有元素,我们可以显示顺序图。 下一个图表显示了从SDP offer-answer协商开始的对等体,STUN和TURN服务器之间交换的消息。
一旦SDP协商完成,两个对等体已经建立了媒体如何交换,并且会话被认为是已经初始化。然而,对等体需要找到一种在它们之间发送协议的媒体的方式。他们仍然缺乏关于彼此的网络连接的信息:IP和端口,如果在NAT之后,可以使用中继服务器...该信息以ICE候选者的形式交换,详述可用的通信方法,并且根据RF5245 ,描述了ICE过程。
候选者需要保存用于交换媒体(IP,端口和优先级高于其他信道)信道的信息。由于对每个连接单独地收集它们,所以该过程可能花费相当长的时间。因此,一旦它们被发现,它们被异步地发送,其他对等体可以尽快探测每个候选者。 ICE协议的这种优化称为Trickle ICE。
webRtcEndpoint.addOnIceCandidateListener(new EventListener<OnIceCandidateEvent>() {
@Override
public void onEvent(OnIceCandidateEvent event) {
sendIceCandidateToRemotePeer(event.getCandidate());
}
});
webRtcEndpoint.gatherCandidates();
从另一个对等体接收的远程候选必须由WebRtcEndpoint处理。 这样,Kurento将知道从连接到哪里发送媒体和控制包。
webRtcEndpoint.addIceCandidate(iceCandidate);
带宽控制
WebRTC允许您为视频使用不同的编解码器。如果使用VP8(默认)或VP9,它们是带宽自适应的,这意味着编解码器本身将根据网络条件调整比特率。端点开始以低比特率交换媒体,并且在网络保持稳定时缓慢增加比特率。对等体提供彼此关于其自己的连接结束的反馈。两端的条件可能不一样,因为连接可能不对称(上传通常低于下载带宽),带宽需要与其他程序或端点共享...此外,WebRTC链路可能不是双向的:仅发送/只接收配置是可能的。在考虑带宽使用时,必须考虑所有这些。
所以从端点的角度来看,有两个方向:传入和传出。每个方向的比特率可以独立修改,因此可以为不同类型的媒体(音频和视频)的比特率。
Kurento WebRTC端点适配传输到网络条件。它们开始发送低比特率,并根据对等体报告的可用带宽缓慢增加。以相同的方式,他们发送报告与自己的带宽测量,因此同行可以做同样的。每个方向的比特率可以独立修改,因此可以为不同类型的媒体(音频和视频)的比特率。
** [*]输入带宽控制机制: **Kurento WebRTC允许配置接收比特率的范围。最大值在SDP中公布,而min值设置为限制REMB包的较低值。因此,min值将只有效果.
- setMin/MaxVideoRecvBandwidth:设置接收视频流预期的最大/最大比特率限制。
- setMin/MaxAudioRecvBandwidth:设置接收音频流预期的最大/最大比特率限制。
** [*]输出带宽控制机制: **用于控制发送到远程对等体的输出媒体流的比特率的配置间隔。重要的是要记住,推送比特率取决于网络和远程对等能力。远程对等体可以在它们的SDP中通告带宽限制(通过b =
- setMin/MaxVideoSendBandwidth:设置发送到远程对等体的视频的最小/最大比特率限制。
- setMin/MaxAudioSendBandwidth:设置发送到远程对等体的音频的最小/最大比特率限制。
有带宽控制参数必须在SDP协商发生之前更改,并且以后不能更改。默认视频比特率范围在100kbps和500kbps之间。
从Kurento端点的角度来看,可以有三种可能的设置:
- send-only:端点将仅向远程对等体发送媒体。可用的出站带宽很重要。
- recv-only:端点将只接收来自远程对等体的媒体。可用的输入带宽很重要。
- send-recv:端点将向远程对等体发送和接收媒体。可用的输出和输入带宽很重要。
媒体流控制
有了SDP协商,ICE候选人,TURN,STUN ...你可能想知道:我怎么知道什么时候两个对等体之间的连接已经建立? 而且,我怎么知道它什么时候穿透了? Kurento中的WebRtcEndpoint提供了注册到媒体流相关事件的可能性。 订阅这些事件允许用户在端点之间的连接被建立或丢失时采取必要的动作,等等。
与连接状态的变化相关的事件是:
** 1. IceComponentStateChange **: 此事件仅通知ICE连接状态的更改。 可能的值为:
- DISCONNECTED:未计划任何活动。
- GATHERING:收集本地候选人。
- CONNECTING:建立连接。
- CONNECTED:至少一个工作候选对。
- READY:ICE论断,最终候选对。
- FAILED:连接检查已完成,但连接未建立。
可以说它是仅网络的,因为它只考虑网络连接的状态,忽略其他更高级别的东西,例如DTLS握手,RTCP流等。这意味着,当组件状态为CONNECTED时, 可能没有媒体在对等体之间流动。 这使得此事件仅用于接收关于对等体之间的连接的低级信息。 更重要的是,虽然其他事件可能会在触发前留下一段优雅的时间,但此事件在检测到状态更改后立即触发。
webRtcEndpoint.addIceComponentStateChangeListener(
new EventListener<IceComponentStateChangeEvent>() {
@Override
public void onEvent(IceComponentStateChangeEvent event) {
// Your implementation goes here
}
});
** 2. IceGatheringDone ** :当ICE收获过程完成时引发。 这意味着所有候选主机,srflx和relay已经被发现。
webRtcEndpoint.addIceGatheringDoneListener(new EventListener<IceGatheringDoneEvent>() {
@Override
public void onEvent(IceGatheringDoneEvent event) {
// Your implementation goes here
}
});
** 3. NewCandidatePairSelected **:在选择新的ICE候选对时引发。 该对包含用于连接的本地和远程候选。 如果在链路中找到具有较高优先级的新的候选对,则可以在媒体会话期间产生该事件。
webRtcEndpoint.addNewCandidatePairSelectedListener(new EventListener<NewCandidatePairSelectedEvent>() {
@Override
public void onEvent(NewCandidatePairSelectedEvent event) {
// Your implementation goes here
}
});
** 4. MediaFlowOutStateChangeEvent **:当媒体元素的输出中存在媒体时,更改为FLOWING。 例如,当WebRTC端点开始接收媒体时,将使用FLOWING触发此事件,因为将有媒体准备好发送到其他端点。
webRtcEndpoint.addMediaFlowOutStateChangeListener(new EventListener<MediaFlowOutStateChangeEvent>() {
@Override
public void onEvent(MediaFlowOutStateChangeEvent event) {
// Your implementation goes here
}
});
** 5. MediaFlowInStateChangeEvent**:当有另一个端点将媒体送到此端点时,更改为FLOWING。
webRtcEndpoint.addMediaFlowInStateChangeListener(new EventListener<MediaFlowInStateChangeEvent>() {
@Override
public void onEvent(MediaFlowInStateChangeEvent event) {
// Your implementation goes here
}
});
为了更好地理解最后两个事件,我们将使用一个例子。 如果你有这条线:
webrtc1.connect(webrtc2)
假设webrtc1正在接收媒体,将有webrtc1触发事件MediaFlowOutStateChangeEvent,而webrtc2触发MediaFlowOutStateChangeEvent和MediaFlowInStateChangeEvent。 每个事件对于连接的媒体类型(AUDIO和/或VIDEO)触发一次。
注册这些事件,意味着需要提供回调函数,在这些事件状态更改时中被调用。 通知每个事件的新状态。 虽然两者都提供不同级别关于连接的信息,但在大多数情况下,它只能依赖于MediaStateChangedEvent,因为它是产生更有意义的信息。