前几天使用EasyRTC框架的Demo来测试音视频通信,虽然点对点视频连通,流程也大体知道,但是感觉还是对于WebRTC技术还不那么清晰,此时有两个方向可以继续学习,阅读EasyRTC框架,或者直接阅读WebRTC的API,自己实现视频通信流程
权衡后我选择第二种,知其然也要知其所以然,对一门技术要足够熟悉,以后使用的时候才能得心应手,事实也证明,有了上一篇博客内记录的知识点,对进一步了解WebRTC很有帮助,阅读WebRTC的示例,扩展调试都方便多了
1,运行WebRTC示例
https://webrtc.github.io/samples/src/content/peerconnection/pc1/
这个例子的代码中模拟了两台电脑建立点对点连接,在一个页面显示两个视频
如下对代码进行了改造,引入了socket.io,负责中转他们的协商信息,既然要通信,肯定要用两台电脑才好,官方的示例代码里没找到,不过改一改就可以了
2,再造后的代码
主要的代码:Github Gist
点击Start可以开启本地摄像头并使程序处于准备状态可以接受远程的视频, 点击Call可以发起视频请求, 点击Hang Up可以停止接受远程的视频连接
PC1,PC2都点击Start,然后PC1点击Call,就能建立连接通信了
需要注意的是,turn/stun服务器是必须的,localPeerConnection需要通过ice来选定turn/stun服务器来协调路径,即使是局域网中,没有它也不能建立连接,这个服务器可以使用coTurn搭建
3,WebRTC通信流程
原理就是使用RTCPeerConnection创建准备点对点通信的管道,连接的时候设置turn/stun服务器信息,服务器就开始协调候选人(candidate)信息,也就是上一篇文章(WebRTC与EasyRTC(视频聊天流程梳理))里提及的TCP打洞
RTCPeerConnection创建后会有一个监听,icecandidate,ICE会逐步的返回协调到客户端的路径信息到这个监听,然后PC1客户端收到这个icecandidate需要把这个信息发送给PC2,PC2初始化后的RTCPeerConnection
获取到这个candidate信息后,PC1会把它发送给PC2,PC2收到PC1的路径信息后会将这个路径信息通过addIceCandidate方法添加到localPeerConnection,发送这个candidate信息就可以使用Socket.io
之后获取本地媒体的音视频轨(track),将它们添加到localPeerConnection对象中,这也就是要点对点通信后传输出去的媒体信息
在点击Call的时候,PC1使用createOffer方法创建自己的SDP信息,这个SDP是一些描述文本,用来说明自己发送的视频什么格式,有哪些轨道,支持什么编解码等等
...
a=group:BUNDLE audio video
a=msid-semantic: WMS HNWGnpqStY44w8vHQZ3BE1VsRXmuajjk1pXm uQXvAPEH23ysg7WvwxKVmyzdZchzQpwGYTd9
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:suJZ
a=ice-pwd:JLjmrwlB12Tr9MJb6B/WGQwy
a=ice-options:trickle
a=fingerprint:sha-256 80:DD:A7:A8:D4:16:CA:9B:1F:79:9A:0D:7B:05:EA:E7:35:FD:11:6F:B8:69:C0:57:0F:77:2B:D2:AE:0B:02:E3
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
...
a=ssrc:3174753737 cname:u6uhaN/KTBYk6jwt
a=ssrc:3174753737 msid:uQXvAPEH23ysg7WvwxKVmyzdZchzQpwGYTd9 f595523e-2fcd-400f-992e-0a127753952f
a=ssrc:3174753737 mslabel:uQXvAPEH23ysg7WvwxKVmyzdZchzQpwGYTd9
a=ssrc:3174753737 label:f595523e-2fcd-400f-992e-0a127753952f
a=ssrc:1773101601 cname:u6uhaN/KTBYk6jwt
a=ssrc:1773101601 msid:HNWGnpqStY44w8vHQZ3BE1VsRXmuajjk1pXm bbf350aa-714c-40f2-a646-6d235d154086
a=ssrc:1773101601 mslabel:HNWGnpqStY44w8vHQZ3BE1VsRXmuajjk1pXm
a=ssrc:1773101601 label:bbf350aa-714c-40f2-a646-6d235d154086
PC2接收到PC1发送的Offer(SDP)信息后,将PC1的Offer信息使用setRemoteDescription方法添加到localPeerConnection,然后开始生成自己的Answer(SDP)信息,将自己的SDP信息通过setLocalDescription方法添加到localPeerConnection,然后将Answer通过socket.io发送给PC1,PC1之前添加过自己的SDP,又收到PC2的SDP,现在localPeerConnection对象中包含了远程主机的路径信息,和两方的SDP信息
PC1及PC2的localPeerConnection实例接收到track事件后,获取远程视频,点对点连接建立完成
上边流程图和代码有些出入,因为SDP信息,candidate信息没什么先后顺序,只要都发送给通信对方就OK了
4,使用Docker启动coTurn服务器
我使用的是这个镜像:https://hub.docker.com/r/instrumentisto/coturn
新建一个文件my.conf
listening-ip=172.27.0.3
listening-port=3478
relay-ip=172.27.0.3
external-ip=118.24.101.22
relay-threads=500
lt-cred-mech
pidfile="/var/run/turnserver.pid"
min-port=49152
max-port=65535
user=username:123456
realm=Aha
这里的172.27.0.3是服务器的内网地址,118.24.101.22是公网地址
coTurn服务安装后自带stun/turn两种协议支持,stun地址为stun:118.24.101.22:3478,turn需要密码,地址是turn:118.24.101.22:3478
启动
docker run -d --network=host --name=coturn -v $(pwd)/my.conf:/etc/coturn/turnserver.conf instrumentisto/coturn --no-cli
5,使用Electron运行的优点
Electron即Chromium浏览器,在WebRTC技术中,将开发完成的网页放在Electron壳里运行,有如下优点
- 封装成桌面APP,功能会更灵活多样
- 如果调用摄像头的代码是远程的,需要远程页面是的HTTPS的,或者localhost本地页面才行,如果在局域网内应用,没必要自签证书部署HTTPS,就算信任了自签证书,浏览器还是会有安全警告,而使用Electron加载index.html解决了这个问题
- Electron启动后,打开摄像头不会出现浏览器那样的权限请求,这个就很方便实用
两台主机点对点连接就是这样了,如果是再多一个电脑通信,那么就需要新建一个RTCPeerConnection,然后再走一遍流程