webrtc 一对一视频服务搭建

WebRTC背景

WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准。

客户端

创建RTCPeerConnection实例->发送candidate->创建offer->交换offer->添加媒体流

客户端主要工作简单来看就是三个交换,交换ice、交换offer、交换流。通过代码来看:

  • 创建WebRTC实例

    去除浏览器前缀转换通用变量

    const RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    const IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
    const SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
    navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
    

    创建WebRTC peer实例,它接收一个配置选项configuration,其中配置了用来创建连接的 ICE 服务器信息。

    const configuration = {
        // 内网穿透使用sturn和turn服务信息,下面的服务器国内无法使用,需要自行搭建,见后文
        iceServers: [
            {urls: "stun:23.21.150.121"},
            {urls: "stun:stun.l.google.com:19302"},
            {urls: "turn:numb.viagenie.ca", credential: "webrtcdemo", username: "louis%40mozilla.com"}
        ]
    }
    const pc = new RTCPeerConnection(configuration);
    // 当 ICE 框架找到可以使用 Peer 创建连接的 “候选者”,会立即触发一个事件(onicecandidate)。ICE候选者信息通过信令服务器转发
    pc.onicecandidate = function (e) {
        if (!e.candidate) return;
        // 通过信令服务器转发candidate 接收方通过addIceCandidate方法添加
        send("icecandidate", JSON.stringify(e.candidate));
    };
    
    // ... 
    function addIce(candidate){
        // 信令服务器响应后 将远端的candidate加入本地ice
        pc.addIceCandidate(new RTCIceCandidate(candidate));
    }
    
    
  • ICECandidate

    ICE全称Interactive Connectivity Establishment:交互式连通建立方式。

    ICE参照RFC5245建议实现,是一组基于offer/answer模式解决NAT穿越的协议集合。
    它综合利用现有的STUN,TURN等协议,以更有效的方式来建立会话。

    客户端侧无需关心所处网络的位置以及NAT类型,并且能够动态的发现最优的传输路径。

    简单来说 发起方创建RTCPeerConnection实例后要将自己的candidate发送给接收方,双方互换candidate才可建立链接。

    // 添加ice 
    pc.addIceCandidate(new RTCIceCandidate(candidate))
    

推荐博客 ICE

  • 交换offer SDP

    Offer SDP 是用来向另一端描述期望格式(视频, 格式, 解编码, 加密, 解析度, 尺寸 等等)的元数据。

    一次信息的交换需要从一端拿到 offer,然后另外一端接受这个 offer 然后返回一个 answer。

    // 创建offer
    await pc.createOffer({
        // 是否接收音频
        offerToReceiveAudio: true,
        // 是否接收视频
        offerToReceiveVideo: true
    });
    // 添加至本地描述
    await pc.setLocalDescription(offer);
    
    // ... 将本地的offer通过信令服务器转发给接收方
    
    // 接收方收到远端的offer 创建自己answer 并发送给主叫 
    await recvPc.setRemoteDescription(offer);
    await recvPc.setLocalDescription(await recvPc.createAnswer());
    
    // ...将answer发送给主叫
    
    // ...接收到被叫方返回的answer信息添加到setRemoteDescription
    pc.setRemoteDescription(answer);
    
    
  • 添加流发送给对方

    在接收到offer时我们就可以获取本地的媒体流,并添加到RTCPeerConnection实例中

    //获取媒体流 如果只想使用默认配置video直接赋值为true
    window.navigator.mediaDevices.getUserMedia({video: {
            // 帧数 
            frameRate: { ideal: 10, max: 15 },
            // 宽高 最大1280 最小800 ideal(应用最理想的)值时,这个值有着更高的权重,意味着浏览器会先尝试找到最接近指定的理想值的设定或者摄像头(如果设备拥有不止一个摄像头)。此外还要facingMode 前置或者后置摄像头
            width: { max: 1280,ideal: 1024,min:800 },
            height: { max: 720,ideal: 960,min:600 }
        },audio:true})
    .then(mediaStream => {
        console.log('addStream');
        // 添加流到RTCPeerConnection实例中
        this.pc.addStream(mediaStream);
        // 把本地的流添加到video标签中播放
        $('.my-video').srcObject = mediaStream;
    }); 
    
    // 监听onaddstream
    pc.onaddstream=function(e){
        // e.stream 
        $('.your-best-friend-video').srcObject = e.stream;
    }
    

推荐阅读MDN教程

推荐博客 浅显易懂

获取媒体流 getUserMedia MDN教程

查看客户端代码

信令服务器

为客户端交换offer交换ice的中转站

使用nodejs创建一个简单的WebSocket服务,如果想要在公网上必须要用wss,因为客户端用的是https协议无法与ws协议连接,下面是如何创建wss:


const WebSocket = require('ws');
const https = require('https');
const options={
    key:fs.readFileSync('./ssl/privkey.key','utf8'),
    cert:fs.readFileSync('./ssl/cert.crt','utf8')
}
// 添加证书及私钥 证书可以使用openSSL生成,或者申请免费的
const options={
    key:fs.readFileSync('./ssl/privkey.key','utf8'),
    cert:fs.readFileSync('./ssl/cert.crt','utf8')
}
// 创建https服务
const httpsServer=https.createServer(options, function (req, res) {
    //要是单纯的https连接的话就会返回这个东西
    res.writeHead(403);//403即可
    res.end("This is a  WebSockets server!\n");
}).listen(15065)
// 创建wss服务
const wsServer = new WebSocket.Server({
    server: httpsServer
});
wsServer.on('connection', (client, request) => {
    // 发送信息
    client.send('xxx')
    // 接收消息
    client.on('message', msg => {})
    // 关闭连接
    client.on('close', msg => {})
})

查看信令服务服务器代码

STURN/TURN中继服务器

客户端在局域网环境下想要与外网的机器通讯,需要NAT网络穿越。

  • coturn是谷歌开源的中继服务器应用,集合了sturn 和 turn。

  • NAT两种类型,对称NAT与非对称NAT:

    非对称NAT,每次请求对应的IP端口是不对应的(海王) 使用sturn就可以完成打洞穿越。
    
    对称NAT,每次请求对应的IP端口是对应的(5201314),打洞穿越难度大 需要使用turn服务器中继。
    
    通过一个场景解释:客户端向sturn服务器发请求,获得自己的对外ip和端口,此时客户端是对称NAT,这个对外暴露的端口只能给服务器使用,被叫客户端无法使用主叫暴露的端口导致无法连接。如果是非对称的,sturn服务器获得主叫对外暴露的IP和端口,告诉其他客户端,其他客户端就可以拿着这个ip和端口进行连接。当然非对称也有限制,具体分为三种,阅读这篇文章[对称与非对称NAT](https://blog.csdn.net/yifuteli_kevin/article/details/8911261?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control)。
    
  • sturn和turn区别:

    sturn帮助客户端寻址,客户端请求sturn服务器,返回给客户端对外暴露的ip和端口,使客户端可以相互寻址,并p2p连接
    
    turn 采用中继的方式,占用资源大
    
  • 建议在客户端strun和turn都要配置

  • 下载 安装coturn

    下载libevent-2.0 里面有coturn依赖的库

    wget https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
    tar zxvf libevent-2.0.21-stable.tar.gz
    cd libevent-2.0.21-stable && ./configure
    make && make install
    

    下载编译coturn

    git clone https://github.com/coturn/coturn
    cd coturn 
    ./configure 
    make 
    make install
    
  • 配置

    coturn有多种配置方式:命令行、conf文件,数据库。我们使用配置文件的方式。进入/usr/local/etc/目录下有turnserver.conf.default,复制为turnserver.conf

    此外还需要OpenSSL

    yum install openssl 
    

    使用OpenSSL生成cert和key

    openssl req -x509 -newkey rsa:2048 -keyout /etc/turn_server_pkey.pem -out /etc/turn_server_cert.pem -days 99999 -nodes 
    

    获取网卡信息ifconfig,记下内网地址

    进入配置文件并修改

    relay-device=eth0   #与前ifconfig查到的网卡名称一致
    listening-ip=172.18.77.60    #内网IP
    listening-port=3478
    external-ip=47.107.110.xxx    #公网IP
    relay-threads=50
    cert=/etc/turn_server_cert.pem
    pkey=/etc/turn_server_pkey.pem
    min-port=49152
    max-port=65535
    user=user:123456    #用户名密码,创建IceServer时用
    realm=xxxxxx
    

    启动turnserver

     turnserver -o -a -f -user=user:123456 -r cwtlab
    
  • 测试

    可以使用自带的工具进行测试也可以通谷歌提供的测试sturn/turn网站

    使用自动工具

    # 测试 STUN
    turnutils_stunclient 47.107.110.xxx
    # 测试 TURN
    turnutils_uclient -u user -w 123456 47.107.110.xxx
    
  • 进入源码所根目录查看相关文档

    README.turnserver 查看turnserver相关指令及介绍

    README.turnadmin 查看turnadmin 相关指令及介绍

    README.turnutils 查看turnutils 测试

    相关程序位置bin目录下

coturn 下载安装 配置 博客

coturn 下载安装 配置二

对称与非对称NAT

谷歌提供的测试sturn/turn网站

你可能感兴趣的:(webrtc 一对一视频服务搭建)