初探webRTC(1)--webRTC介绍与实现网页自拍

webRTC 背景知识

webRTC 是什么

webRTC 的目的是实现实时通信大众化。其无需借助第三方软件或插件便可在开放网络中传输高质量音视频流,我们可以通过 webRTC 在浏览器内部的 api 中内建一些构建实时通信应用的库或框架,webRTC 在浏览器 API 中集成了大量技术,解决了很多繁重的问题,例如:捕捉摄像头和麦克风,音视频编解码,传输层以及会话管理。

webRTC 在浏览器中实现的 api

  • getUserMedia
    从客户摄像头或麦克风获取数据流。
    在 Chrome、Opera、FireFox 和 Edge 中已经实现,我们可以使用 getUserMedia 作为音频的输入。
  • RTCPeerConnection
    初始化一个连接,连接他人以及传送流媒体信息
    在 Chrome、Opera 和 FireFox 中实现。关于名字的解释:经过几次迭代,RTCPeerConnection 被 Chrome 和 Opera 实现为 webkitRTCPeerConnection,被 FireFox 实现为 mozRTCPeerConnection。其他的名字都是过时的,当标准稳定时,前缀会被删除。

  • RTCDataChannel
    P2P 数据传输管道。

webRTC 需要做的事情

  • 获取音视频流或其他数据流。
  • 获取网络信息例如 IP 地址和端口号,与其他客户端交换这些信息来进行连接,包括穿透防火墙。
  • 向 signaling 报告错误或启动会话关闭连接等。
  • 对于分辨率解码器等与其他客户端交换信息。
  • 传输音视频或其他数据信息。

建立一个简单的 webRTC 应用

使用 UDP 传输协议

SDP:会话描述协议

寻找一条清晰线路的方法

  • NAT 会话穿透工具(STUN)
    STUN 是双方建立稳定连接的第一步,首先发一个请求给服务器,开启 STUN 协议,之后服务器识别发出请求的客户端的 ip,将其返回给客户端。
    使用 STUN 协议需要一个支持 STUN 协议的服务器,目前,在 Firefox 和 Chrome 中,浏览器直接提供了支持此协议的默认服务器。
  • 使用中继技术穿透 NAT(TURN)
    主要解决受防火墙限制的问题。
    其工作原理是代表客户端在对等连接的双方之间增加一个转播,要求 TURN 服务器去下载,处理并重定向每个用户发来的数据包,建立高质量的服务代价很高。
    一般情况下,不使用 TURN 是没有问题的
  • 交互式连接建立(ICE)
    ice 将 STUN 和 TURN 结合在一起。
    每一个 ICE 候选路径都是通过 STUN 和 TURN 来找到的。通过查询 stun 服务器来找到外部 ip 地址,并附上 stun 服务器的地址,当连接失败时可用做备份。当浏览器找到一个新的候选路径时,通知客户端程序将使用信令信道来发送 ice 候选路径,当足够多的地址被发现且通过验证,并连接建立之后,这个过程也就结束了。

建立的工作流程

  1. 判断浏览器是否支持,不支持即报错
  2. 获取用户的媒体流
  3. 创建对等连接对象
  4. 添加 ICE 处理器
  5. 开始 offer 和丹辉
  6. 创建连接

下面来介绍一个网页自拍

该说的都写在注释里面啦~~~
注释很详细喔

index.html 如下

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body {
background-color: #5cc9f5;
margin-top: 15px;
}
video {
background-color: black;
border: 1px solid gray;
}
#containers {
position: relative;
display: block;
width: 500px;
height: 500px;
margin: 0 auto;
}
#yours {
width: 150px;
height: 150px;
position: absolute;
top: 15px;
right: 15px;
}
#theirs {
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<div class="container">
<video id="yours" autoplay></video>
<video id="theirs" autoplay></video>
</div>
<script src="./main.js"></script>
</body>
</html>

main.js 如下

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//为了兼容不同浏览器,因为每个浏览器的hasGetUserMedia不同
function hasGetUserMedia() {
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
return !!navigator.getUserMedia;
}
function hasRTCPeerConnection() {
window.RTCPeerConnection =
window.RTCPeerConnection ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection;
return !!window.RTCPeerConnection;
}
//选中对应的html标签
var yourVideo = document.querySelector("#yours"),
theirVideo = document.querySelector("#theirs"),
yourConnection,
theirConnection;
if (hasGetUserMedia()) {
/**
* 这里用到了很重要的一个api getUserMedia
* 第一个参数是一个对象,即约束对象,一些配置等,参见代码
* 第二个参数是成功回调,参数为流stream
* 第三个参数是失败回调
*/
navigator.getUserMedia(
{
video: true,
audio: false
},
function(stream) {
//注释掉的表示该api已经被chrome抛弃了,用下面那行的就可以了
// yourVideo.src = window.URL.createObjectURL(stream);
yourVideo.srcObject = stream;
yourVideo.play();
if (hasRTCPeerConnection()) {
//维护会话和对等连接的状态
startPeerConnection(stream);
} else {
alert("Sorry,your brower dont support WebRTC");
}
},
function(error) {
console.log("error", error);
}
);
} else {
alert("Sorry,your brower dont support WebRTC");
}
/**
* 这个函数的作用
* 创建RTCPeerConnection对象
* 建立SDP offer 并返回
* 为双方寻找ICE候选路径
*/
function startPeerConnection(stream) {
var configuration = {
//使用默认的配置
// "iceServers":[{"url":"stun:127.0.0.1:9876"}]
};
//建立两个RTCPeerConnection对象
yourConnection = new webkitRTCPeerConnection(configuration);
theirConnection = new webkitRTCPeerConnection(configuration);
//创建ICE处理,寻找候选路径
yourConnection.onicecandidate = function(e) {
if (e.candidate) {
theirConnection.addIceCandidata(new RTCIceCandidate(e.candidate));
}
};
theirConnection.onicecandidate = function(e) {
if (e.candidate) {
yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate));
}
};
//开始offer
yourConnection.createOffer(function(offer) {
yourConnection.setLocalDescription(offer);
theirConnection.setRemoteDescription(offer);
//创建应答
theirConnection.createAnswer(function(offer) {
theirConnection.setLocalDescription(offer);
yourConnection.setRemoteDescription(offer);
});
});
}