webrtc 一对一视频服务搭建

WebRTC背景

WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它于2011年6月1日开源并在GoogleMozillaOpera支持下被纳入万维网联盟的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网站

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容