8┃音视频直播系统之 WebRTC 信令系统实现以及通讯核心并实现视频通话


一、信令系统

  • 信令系统主要用来进行信令的交换

  • 在通信双方彼此连接、传输媒体数据之前,它们要通过信令服务器交换一些信息,如规范协商

  • 若 A 与 B 要进行音视频通信,那么 A 要知道 B 已经上线了,同样,B 也要知道 A 在等着与它通信呢

  • 只有双方都知道彼此存在,才能由一方向另一方发起音视频通信请求,并最终实现音视频通话

  • 客户端代码如下:

  • 第一步:首先弹出一个输入框,要求用户写入要加入的房间

  • 第二步:通过 io.connect() 建立与服务端的连接

  • 第三步:再根据 socket 返回的消息做不同的处理





    
    
    
    信令系统








  • 服务端代码如下:

  • 需要通过 npm install socket.io 安装socket模块

  • 需要通过 npm install node-static 安装socket模块,使服务器具有发布静态文件的功能

  • 服务端侦听 2022 这个端口,对不同的消息做相应的处理

const static = require('node-static');
const http = require('http');
const file = new (static.Server)();

const app = http.createServer(function (req, res) {
    file.serve(req, res);
}).listen(2022);

// 侦听 2022
const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => {
    // convenience function to log server messages to the client
    function log() {
        const array = ['>>> Message from server: '];
        for (var i = 0; i < arguments.length; i++) {
            array.push(arguments[i]);
        }
        socket.emit('log', array);
    }

    socket.on('message', (message) => {
        // 收到 message 时,进行广播
        log('Got message:', message);
        // for a real app, would be room only (not broadcast)
        socket.broadcast.emit('message', message); // 在真实的应用中,应该只在房间内广播
    });

    socket.on('create or join', (room) => {
        // 收到 “create or join” 消息
        var clientsInRoom = io.sockets.adapter.rooms[room];
        var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;

        log('Room ' + room + ' has ' + numClients + ' client(s)');
        log('Request to create or join room ' + room);

        if (numClients === 0) {
            // 如果房间里没人
            socket.join(room);
            // 发送 "created" 消息
            socket.emit('created', room);
        } else if (numClients === 1) {
            // 如果房间里有一个人
            io.sockets.in(room).emit('join', room);
            socket.join(room);
            // 发送 “joined”消息
            socket.emit('joined', room);
        } else {
            // max two clients
            // 发送 "full" 消息
            socket.emit('full', room);
        }

        socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
        socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);
    });
});

二、RTCPeerConnection

  • RTCPeerConnection 类是在浏览器下使用 WebRTC 实现 1 对 1 实时互动音视频系统最核心的类

  • 它是WebRTC传输音视频和交换数据的API

  • RTCPeerConnection 就与普通的 socket 一样,在通话的每一端都至少有一个RTCPeerConnection 对象。在 WebRTC 中它负责与各端建立连接,接收、发送音视频数据,并保障音视频的服务质量

三、实现视频通话

  • 为连接的每个端创建一个 RTCPeerConnection 对象,并且给 RTCPeerConnection 对象添加一个本地流,该流是从 getUserMedia() 获取的

  • 获取本地媒体描述信息,即 SDP 信息,并与对端进行交换

  • 获得网络信息,即 Candidate(IP 地址和端口),并与远端进行交换





    
    
    
    实现视频通话



    
    

    

四、视频通话流程详解

  • 视频通话本是不同的端与端连接,上面的代码在同一个浏览器中模拟多端连接的情况,可以通过开两个标签页,来模拟pc1端和pc2端

  • 所以大家会看到两个视频是一摸一样的,但是它的整个底层都是从本机自己IO的那个逻辑网卡转过来的

  • 当调用 call 的时候就会调用双方的 RTCPeerConnection

  • 当这个两个 PeerConnection 创建完成之后,它们会作协商处理

  • 协商处理完成之后进行 Candidate 采集,也就是说有效地址的采集

  • 采集完了之后进行交换,然后形成这个Candidate pair再进行排序

  • 然后再进行连接性检测,最终找到最有效的那个链路

  • 之后就将 localVideo 展示的这个数据通过 PeerConnection 传送到另一端

  • 另一端收集到数据之后会触发 onAddStream 或者 onTrack 就是说明我收到数据了,那当收到这个事件之后

  • 我们再将它设置到这个 remoteVideo 里面去

  • 这样远端的这个 video 就展示出来了,显示出我们本地采集的数据了