Web Real-Time Communication(Web实时通信,WebRTC)由一组标准、协议和JavaScript API组成,用于实现浏览器之间(端到端)的音频、视频及数据共享。

WebRTC使得实时通信变成一种标准功能,任何Web应用都无需借助第三方插件和专有软件,而是通过简单地JavaScript API即可完成。

在WebRTC中,有三个主要的知识点,理解了这三个知识点,也就理解了WebRTC的底层实现原理。这三个知识点分别是:

  • MediaStream:获取音频和视频流
  • RTCPeerConnection:音频和视频数据通信
  • RTCDataChannel:任意应用数据通信

MediaStream

如上所说,MediaStream主要是用于获取音频和视频流。其JS实现也比较简单,代码如下:

'use strict';

navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia; var constraints = { // 音频、视频约束
audio: true, // 指定请求音频Track
video: { // 指定请求视频Track
mandatory: { // 对视频Track的强制约束条件
width: {min: 320},
height: {min: 180}
},
optional: [ // 对视频Track的可选约束条件
{frameRate: 30}
]
}
}; var video = document.querySelector('video'); function successCallback(stream) {
if (window.URL) {
video.src = window.URL.createObjectURL(stream);
} else {
video.src = stream;
}
} function errorCallback(error) {
console.log('navigator.getUserMedia error: ', error);
} navigator.getUserMedia(constraints, successCallback, errorCallback);

  

在JS中,我们通过getUserMedia函数来处理音频和视频,该函数接收三个参数,分别是音视频的约束,成功的回调以及失败的回调。

在底层,浏览器通过音频和视频引擎对捕获的原始音频和视频流加以处理,除了对画质和音质增强之外,还得保证音频和视频的同步。

由于音频和视频是用来传输的,因此,发送方还要适应不断变化的带宽和客户端之间的网络延迟调整输出的比特率。

对于接收方来说,则必须实时解码音频和视频流,并适应网络抖动和时延。其工作原理如下图所示:

如上成功回调的stream对象中携带者一个或多个同步的Track,如果你同时在约束中设置了音频和视频为true,则在stream中会携带有音频Track和视频Track,每个Track在时间上是同步的。

stream的输出可以被发送到一或多个目的地:本地的音频或视频元素、后期处理的JavaScript代理,或者远程另一端。如下图所示:

RTCPeerConnection

在获取到音频和视频流后,下一步要做的就是将其发送出去。但这个跟client-server模式不同,这是client-client之间的传输,因此,在协议层面就必须解决NAT穿透问题,否则传输就无从谈起。

另外,由于WebRTC主要是用来解决实时通信的问题,可靠性并不是很重要,因此,WebRTC使用UDP作为传输层协议:低延迟和及时性才是关键。

在更深入讲解之前,我们先来思考一下,是不是只要打开音频、视频,然后发送UDP包就搞定了?

当然没那么简单,除了要解决我们上面说的NAT穿透问题之外,还需要为每个流协商参数,对用户数据进行加密,并且需要实现拥塞和流量控制。

我们来看一张WebRTC的分层协议图:

ICE、STUN和TURN是通过UDP建立并维护端到端连接所必需的;SDP 是一种数据格式,用于端到端连接时协商参数;DTLS用于保障传输数据的安全;SCTP和SRTP属于应用层协议,用于在UDP之上提供不同流的多路复用、拥塞和流量控制,以及部分可靠的交付和其他服务。

ICE(Interactive Connectivity Establishment,交互连接建立):由于端与端之间存在多层防火墙和NAT设备阻隔,因此我们需要一种机制来收集两端之间公共线路的IP,而ICE则是干这件事的好帮手。

  • ICE代理向操作系统查询本地IP地址
  • 如果配置了STUN服务器,ICE代理会查询外部STUN服务器,以取得本地端的公共IP和端口
  • 如果配置了TURN服务器,ICE则会将TURN服务器作为一个候选项,当端到端的连接失败,数据将通过指定的中间设备转发。

WebRTC使用SDP(Session Description Protocol,会话描述协议)描述端到端连接的参数。
SDP不包含媒体本身的任何信息,仅用于描述"会话状况",表现为一系列的连接属性:要交换的媒体类型(音频、视频及应用数据)、网络传输协议、使用的编解码器及其设置、带宽及其他元数据。

DTLS对TLS协议进行了扩展,为每条握手记录明确添加了偏移字段和序号,这样就满足了有序交付的条件,也能让大记录可以被分段成多个分组并在另一端再进行组装。
DTLS握手记录严格按照TLS协议规定的顺序传输,顺序不对就报错。最后,DTLS还要处理丢包问题:两端都是用计时器,如果预定时间没有收到应答,就重传握手记录。
为保证过程完整,两端都要生成自己签名的证书,然后按照常规的TLS握手协议走。但这样的证书不能用于验证身份,因为没有要验证的信任链。因此,在必要情况下,
应用必须自己参与各端的身份验证:

  • 应用可以通过登录来验证用户
  • 每一端也可以在生成SDP提议/应答时指定各自的"身份颁发机构",等对端接收到SDP消息后,可以联系指定的身份颁发机构验证收到的证书

SRTP为通过IP网络交付音频和视频定义了标准的分组格式。SRTP本身并不对传输数据的及时性、可靠性或数据恢复提供任何保证机制,
它只负责把数字化的音频采样和视频帧用一些元数据封装起来,以辅助接收方处理这些流。

SCTP是一个传输层协议,直接在IP协议上运行,这一点跟TCP和UDP类似。不过在WebRTC这里,SCTP是在一个安全的DTLS信道中运行,而这个信道又运行在UDP之上。
由于WebRTC支持通过DataChannel API在端与端之间传输任意应用数据,而DataChannel就依赖于SCTP。

以上讲了这么多,终于到我们的主角RTCPeerConnection,RTCPeerConnection接口负责维护每一个端到端连接的完整生命周期:

  • RTCPeerConnection管理穿越NAT的完整ICE工作流
  • RTCPeerConnection发送自动(STUN)持久化信号
  • RTCPeerConnection跟踪本地流
  • RTCPeerConnection跟踪远程流
  • RTCPeerConnection按需触发自动流协商
  • RTCPeerConnection提供必要的API,以生成连接提议,接收应答,允许我们查询连接的当前状态,等等

我们来看一下示例代码:

var signalingChannel = new SignalingChannel();
var pc = null;
var ice = {
"iceServers": [
{ "url": "stun:stun.l.google.com:19302" }, //使用google公共测试服务器
{ "url": "turn:user@turnserver.com", "credential": "pass" } // 如有turn服务器,可在此配置
]
};
signalingChannel.onmessage = function (msg) {
if (msg.offer) { // 监听并处理通过发信通道交付的远程提议
pc = new RTCPeerConnection(ice);
pc.setRemoteDescription(msg.offer);
navigator.getUserMedia({ "audio": true, "video": true }, gotStream, logError);
} else if (msg.candidate) { // 注册远程ICE候选项以开始连接检查
pc.addIceCandidate(msg.candidate);
}
}
function gotStream(evt) {
pc.addstream(evt.stream);
var local_video = document.getElementById('local_video');
local_video.src = window.URL.createObjectURL(evt.stream);
pc.createAnswer(function (answer) { // 生成描述端连接的SDP应答并发送到对端
pc.setLocalDescription(answer);
signalingChannel.send(answer.sdp);
});
}
pc.onicecandidate = function (evt) {
if (evt.candidate) {
signalingChannel.send(evt.candidate);
}
}
pc.onaddstream = function (evt) {
var remote_video = document.getElementById('remote_video');
remote_video.src = window.URL.createObjectURL(evt.stream);
}
function logError() { ... }

  

DataChannel

DataChannel支持端到端的任意应用数据交换,就像WebSocket一样,但是是端到端的。
建立RTCPeerConnection连接之后,两端可以打开一或多个信道交换文本或二进制数据。

其示例demo如下:

var ice = {
'iceServers': [
{'url': 'stun:stun.l.google.com:19302'}, // google公共测试服务器
// {"url": "turn:user@turnservera.com", "credential": "pass"}
]
}; // var signalingChannel = new SignalingChannel(); var pc = new RTCPeerConnection(ice); navigator.getUserMedia({'audio': true}, gotStream, logError); function gotStream(stram) {
pc.addStream(stram); pc.createOffer().then(function(offer){
pc.setLocalDescription(offer);
});
} pc.onicecandidate = function(evt) {
// console.log(evt);
if(evt.target.iceGatheringState == 'complete') {
pc.createOffer().then(function(offer){
// console.log(offer.sdp);
// signalingChannel.send(sdp);
})
}
} function handleChannel(chan) {
console.log(chan);
chan.onerror = function(err) {}
chan.onclose = function() {}
chan.onopen = function(evt) {
console.log('established');
chan.send('DataChannel connection established.');
} chan.onmessage = function(msg){
// do something
}
} // 以合适的交付语义初始化新的DataChannel
var dc = pc.createDataChannel('namedChannel', {reliable: false}); handleChannel(dc);
pc.onDataChannel = handleChannel; function logError(){
console.log('error');
}

  原文:https://segmentfault.com/a/1190000011403597

仅供个人学习和交流

深入理解WebRTC的更多相关文章

  1. (三)WebRTC手记之本地视频采集

    转自:http://www.cnblogs.com/fangkm/p/4374610.html 前面两篇文章介绍WebRTC的运行流程和使用框架接口,接下来就开始分析本地音视频的采集流程.由于篇幅较大 ...

  2. WebRTC手记之本地视频采集

    转载请注明出处:http://www.cnblogs.com/fangkm/p/4374610.html 前面两篇文章介绍WebRTC的运行流程和使用框架接口,接下来就开始分析本地音视频的采集流程.由 ...

  3. 揭开webRTC媒体服务器的神秘面纱——WebRTC媒体服务器&开源项目介绍

    揭开webRTC媒体服务器的神秘面纱--WebRTC媒体服务器&开源项目介绍 WebRTC生态系统是非常庞大的.当我第一次尝试理解WebRTC时,网络资源之多让人难以置信.本文针对webRTC ...

  4. WebRTC 学习资源 电子书 WebRTC权威指南 Learning WebRTC

    webRTC源码下载地址:https://pan.baidu.com/s/18CjClvAuz3B9oF33ngbJIw 提取码:wl1e 1.<WebRTC权威指南>第三版 中文版 本书 ...

  5. webrtc源码阅读理解一

    webrtc是一个比较成熟的实时音视频处理开源项目,一上来老大就扔给我一本webrtc native实践,虽然狠下心"翻"完了一遍,但是还是云里雾里的,在经过几个月的摸索之后,我大 ...

  6. WebRTC学习之ICE深入理解

    ICE(交互式连接建立---Interactive Connectivity Establishment),是一种标准穿透协议.它利用STUN和TURN服务器来帮助端点建立连接.下图显示了ICE的基本 ...

  7. 使用WebRTC搭建前端视频聊天室——数据通道篇

    本文翻译自WebRTC data channels 在两个浏览器中,为聊天.游戏.或是文件传输等需求发送信息是十分复杂的.通常情况下,我们需要建立一台服务器来转发数据,当然规模比较大的情况下,会扩展成 ...

  8. 单独编译使用WebRTC的音频处理模块

    块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...

  9. WebRTC通信流程

    WebRTC是HTML5支持的重要特性之一,有了它,不再需要借助音视频相关的客户端,直接通过浏览器的Web页面就可以实现音视频对聊功能.而且WebRTC项目是开源的,我们可以借助WebRTC源码快速构 ...

随机推荐

  1. IO中断

    >>>io的中断编程 以上例程会有什么问题呢? 注意:CPU内部寄存器

  2. 动画 jquery-transit

    <script type="text/javascript" src="js/jquery.min.js"></script> < ...

  3. spark 机器学习 knn原理(一)

    1.knnK最近邻(k-Nearest  Neighbor,KNN)分类算法,在给定一个已经做好分类的数据集之后,k近邻可以学习其中的分类信息,并可以自动地给未来没有分类的数据分好类.我们可以把用户分 ...

  4. <转> Android LayoutInflater详解

    在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById().不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例 ...

  5. jar命令详解

    原文链接:https://www.cnblogs.com/anyehome/p/9435371.html JAR包是Java中所特有一种压缩文档,其实大家就可以把它理解为.zip包.当然也是有区别的, ...

  6. ERROR 1524 (HY000): Plugin 'auth_socket' is not loaded

    操作系统:Ubuntu 18.04 LTS 数据库:MySQL 5.7 执行了一次修改root用户密码的操作,修改完后退出了数据库,但是,当我在命令行中登录数据库(mysql -u root -p), ...

  7. lombok自定义扩展实践

    lombok是一款能够在java代码编译阶段改变代码的插件.比如生成setter和getter方法,生成log类变量等,能够简化一些特定的模版式代码.本文将以实现一个基于特定注解生成日志代码的方式,简 ...

  8. 使用VisualGDB和OpenOCD调试STM32L0开发板

    本教程主要介绍如何配置VisualGDB和OpenOCD来调试STM32L0开发板的固件,使微控制器进入睡眠模式. 我们将创建一个NUCLEO-L031K6开发板的基本工程,并介绍当CPU进入休眠模式 ...

  9. Zookeeper监控平台

    JMX: 安装配置 1.修改zookeeper的启动脚本 vi  zkServer.sh 找到启动参数ZOOMAIN ​ 修改为下面值(修改一项,新增4项): -Dcom.sun.management ...

  10. linux systemd 从简单的例子入门

    linux systemd 从简单的例子入门 网上很多相关链接,一上来就给一大堆命令和讲解,让人头都大. 我们希望有一个service(服务),让它在开机启动的时候就执行. 用 root 登陆以后: ...