WebRTC学习总结(3):从1v1视频通话的实现说一说RTCPeerConnection的建立

news/2024/6/16 20:25:25 标签: javascript, webrtc

RTCPeerConnection 接口代表一个由本地计算机到远端的WebRTC连接。该接口提供了创建,保持,监控,关闭连接的方法的实现。

一对一视频对话的时候,连接过程如下所示:
在这里插入图片描述
这个过程是不是看起来还挺清晰的?但是这只是表述了媒体信息交换的过程,别忘了还有网络信息~

接下来我们就先好好捋一捋PC建立的过程吧!

  • A打开本地视频流,创建PeerConnection对象,将本地音视频流封装成MediaStream添加到PeerConnection中;

  • A通过CreateOffer创建offer信息,调用setLocalDescription储存本地offer,然后通过信令服务器将offer发送给B;

  • A在调用setLocalDescription的同时,向服务器发送了iceCandidate消息;

  • B打开本地视频流,创建PeerConnection对象,将本地音视频流封装成MediaStream添加到PeerConnection中;

  • B收到offer后,通过setRemoteDescription储存远端offer,然后通过CreateAnswer创建answer信息,同样调用setLocalDescription储存本地answer描述,再将answer发送给A;

  • B在调用setLocalDescription的同时,向服务器发送了iceCandidate消息;

  • A收到B的answer后,再次调用setRemoteDescription设置远端的answer描述。

那接下来就让我们从头看一看,一对一视频对话究竟是怎么实现的吧~ 这里我会按照事件的发送顺序,将客户端和服务器端的代码拆分开说,虽然从逻辑上来讲是对的,但是代码会比较乱,只用于理解,不能直接用哦。

当用户访问对话页面时,会向服务器发送’create or join’消息

服务器收到’create or join’,并计算当前房间里的人数

若 numClients === 0,则向客户端发送’created’消息;客户端收到’created’消息,则将当前用户作为会话发起人,令 isInitiator = true
若 numClients === 1, 则向会话发起人发送’join’消息,发起人端 isChannelReady = true; 向新用户发送’joined’消息,新用户端isChannelReady = true,表示参与对话的两方都准备好了。

上面这一部分的代码在服务器的搭建中都讲过了。

客户端进入网页后会自动打开摄像头:

javascript">navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true
})
    .then(gotLocalMediaStream)
    .catch(function (e) {
        alert('getUserMedia() error: ' + e.name);
    });
function gotLocalMediaStream(mediastream) {
    console.log('Adding local stream.');
    localStream = mediastream;
    localVideo.srcObject = mediastream;
    sendMessage('got user media');
    if (isInitiator) {
        maybeStart();
    }
}

对于会话发起人,在打开摄像头的时候,会发起maybeStart()方法,进而实例化一个RTCPeerConnection,然后将本地的视频流加入pc中。

javascript">function maybeStart() {
    console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
    if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
        console.log('>>>>>> creating peer connection');
        createPeerConnection();
        pc.addStream(localStream);
        isStarted = true;
        console.log('isInitiator', isInitiator);
        if (isInitiator) {
            doCall();
        }
    }
}
function createPeerConnection() {
    try {
        pc = new RTCPeerConnection(null);
        pc.onicecandidate = handleIceCandidate;
        pc.onaddstream = handleRemoteStreamAdded;
        pc.onremovestream = handleRemoteStreamRemoved;
        console.log('Created RTCPeerConnnection');
    } catch (e) {
        console.log('Failed to create PeerConnection, exception: ' + e.message);
        alert('Cannot create RTCPeerConnection object.');
        return;
    }
}
function handleIceCandidate(event) {
    console.log('icecandidate event: ', event);
    if (event.candidate) {
        sendMessage({
            type: 'candidate',
            label: event.candidate.sdpMLineIndex,
            id: event.candidate.sdpMid,
            candidate: event.candidate.candidate
        });
    } else {
        console.log('End of candidates.');
    }
}
function handleRemoteStreamAdded(event) {
    console.log('Remote stream added.');
    remoteStream = event.stream;
    remoteVideo.srcObject = event.stream;
}
function handleRemoteStreamRemoved(event) {
    console.log('Remote stream removed. Event: ', event);
}

这里用到了一个实现定义的方法sendMessage,这个方法可以让一个用户端将信息发送给服务器,之后通过服务器,将这个消息转发给其他的用户端,在整个webRTC通讯过程中发挥了非常大的作用。

javascript">//客户端代码
function sendMessage(message) {
    console.log('Client sending message: ', message);
    socket.emit('message', message);
}
//服务器端相应处理的代码
socket.on('message', function (message) {
    socket.broadcast.emit('message', message);
});

作为会话发起人,要率先执行docall操作,也就是向会话对方发送offer,这个过程中,发起人端会通过setLocalDescription自动生成保存本地音视频相关参数的SDP对象。注意,发起人端在调用setLocalDescription的时候,还触发了handleIceCandidate,向服务器发送了iceCandidate信息。

javascript">function doCall() {
    console.log('Sending offer to peer');
    pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function setLocalAndSendMessage(sessionDescription) {
    pc.setLocalDescription(sessionDescription);
    console.log('setLocalAndSendMessage sending message', sessionDescription);
    sendMessage(sessionDescription);
}
function handleCreateOfferError(event) {
    console.log('createOffer() error: ', event);

发起人端通过sendMessage将offer信息和iceCandidate信息经由服务器发送给接收端,那么相应的接收端会怎么反应呢?接收端,其实大部分时间都是“被动反应”的。它根据接收到的消息,进行相对应的操作。

当接收端接受到来自发起人端的offer类型的message后,即开始初始化自己的peerConnection,
除了将本地视频流添加到pc中,还会将收到的offer信息和candidate信息通过setRemoteDescriptionaddIceCandidate保存起来。
之后,接收端通过doAnswer()向发起人端发送对话描述和candidate消息。

javascript">// This client receives a message
socket.on('message', function (message) {
    console.log('Client received message:', message);
    if (message === 'got user media') {
        maybeStart();
    } else if (message.type === 'offer') {
        console.log("收到offer!"); //接收端
        if (!isInitiator && !isStarted) {
            maybeStart();
        }
        pc.setRemoteDescription(new RTCSessionDescription(message));
        doAnswer();
    } else if (message.type === 'answer' && isStarted) {
        pc.setRemoteDescription(new RTCSessionDescription(message)); //发起人端
    } else if (message.type === 'candidate' && isStarted) {
        console.log("收到candidate!"); //接收端
        var candidate = new RTCIceCandidate({
            sdpMLineIndex: message.label,
            candidate: message.candidate
        });
        pc.addIceCandidate(candidate);
    } else if (message === 'bye' && isStarted) {
        handleRemoteHangup();
    }
});

function doAnswer() {
    console.log('Sending answer to peer.');
    pc.createAnswer().then(
        setLocalAndSendMessage,
        onCreateSessionDescriptionError
    );

发起人端接收到接收端发来的answer和candidate,同样通过setRemoteDescriptionaddIceCandidate保存,这样,参与对话的两方交换了会话信息和ice信息,建立了音视频传输的P2P通道。

建立连接之后,两端的通过上面的handleRemoteStreamAdded方法将接受到的视频流显示到对应的视频标签内。

1v1的视频通话,就这么搞定了。这里展示的代码主要是帮助大家理清RTCPeerConnection建立的流程,但是并不完善,但是网络上完整的项目很多,大家可以多找找~


http://www.niftyadmin.cn/n/1397609.html

相关文章

理论:VLAN与三层交换机--理论讲解

文章目录1.VLAN概述与优势2.VLAN的种类3.静态VLAN的配置1)VLAN的范围2)创建VLAN3)删除VLAN4)配置VLAN,将端口加入VLAN5)VLAN配置实例4.Trunk介绍与配置5.三层交换技术转发原理交换ASIC6.三层交换机的配置前…

WebRTC学习总结(4):多人视频通话的实现思路

在1v1视频通话中,虽然有发起人和接收人的概念,但是消息的发送和接受是“没有对象”的,也就是,通过服务器转发的message中没有指明接受消息的对象,这在房间里最多容纳两个人的前提下不会产生问题,但是在多人…

实验:单臂路由实验,可跟做

文章目录实验工具实验原理实验目的实验步骤实验工具 一台路由器,一台交换机,两台主机 实验原理 VLAN的access接入模式和trunk中继模式,虚拟子接口,(可看上一篇博客) 实验目的 通过这次实验&#xff0c…

实验:利用三层交换机连接局域网与广域网的实验,可跟做

文章目录实验原理实验目的实验工具实验步骤实验原理 三层交换原理,vlan模式,静态路由 实验目的 了解三层交换机的工作原理,对vlan的配置 三层交换 三层交换技术就是:二层交换技术三层转发技术。它解决了局域网中网段划分之后&a…

理论:网络-----路由进阶与安全

文章目录前言:一:动态路由概述二:动态路由协议分类2.1:动态路由协议分类按照路由执行的算法分类按照网关分类三:RIP路由协议工作原理3.1:RIP3.2:RIP路由表的形成3.3:RIP的度量值与更…

实验:rip实验

文章目录实验原理实验目的实验工具实验步骤小结:实验原理 rip协议 实验目的 了解rip协议原理,加深rip配置命令 rip 工作原理 (1)路由建立 路由器运行RIP后,会首先发送路由更新请求,收到请求的路由器会发送自己的RIP路由进行响应…

理论:OSPF路由协议 理论讲解

文章目录前言一:OSPF的基本概念和工作原理1.1:OSPF路由协议概述1.2:OSPF的工作过程1.3:OSPF的基本概念- **Router ID选举规则**ospf的组播地址二:OSPF邻接关系2.1:邻接关系的建立2.2:OSPF的网络…

实验:ospf单域配置实例,配置OSPF实现全网互通

实验原理 ospf原理 实验对象 三台路由器,两台主机,windows10主机,GNS3,crt 实验目的 熟悉ospf的基本命令,了解OSPF的工作原理 OSPF(Open Shortest Path First开放式最短路径优先)是一个内部网关协议(I…