前提
已经搭建好websocket
双端通信(可以先模拟),用于实时交换双方信息。交换的信息也就是所谓的信令。实现webRtc
进行多人会议,屏幕共享、摄像头共享。
我这里定义的websocket信息格式如下
发给某个人,下面会用【消息格式one】指代
{
"body": {
},
"code": "10003",//自定义标识(我自定义区分消息来源用的)
"data": {
"description": {
"type": "answer",
"sdp": "v=0\r\no=- 700908093190320106 2 IN IP4..."
},//需要交换的信息
"meetId": "852229c8c454453da6e0b5e99a8407c8",//会议id
"pageNum": 0,
"pageSize": 0,
"receiveId": "ed986a7b3dbb407e846f76fad909f07d",//接收人Id
"sendId": "c0f1094a363949f88f618f5edb5ecaf8",//发送人Id
"type": "answer"//信息分类
},
"msg": "meetingMessage",
"success": true
}
发给会议中所有人,下面会用【消息格式all】指代
{
"body": {
},
"code": "10003",
"data": {
"meetId": "852229c8c454453da6e0b5e99a8407c8",//会议id
"pageNum": 0,
"pageSize": 0,
"sendId": "c0f1094a363949f88f618f5edb5ecaf8",//发送人Id
"type": "new"//信息分类
},
"msg": "meetingMessage",
"success": true
}
简单说明逻辑
当用户A
进入会议时,向所有人发送【消息格式all】,通知有人加入了会议,然后其他人(取一人B
代指)将主动与A
取得联系。
B
创建一个专门与A
交流的webRtc
连接(new RTCPeerConnection(undefined)
)。将打开的媒体流流加载到连接中B
创建完这个webRtc
连接后生成一个请求连接的信息通过【消息格式one】发给A
,这里面有B
的sdp
信息,并且自己也存一份,发送建立连接请求webRtc
中叫offer
。- 然后
A
收到offer
时,也创建一个专门与B
交流的webRtc
连接(new RTCPeerConnection(undefined)
)。然后将B
的信息存下来,再生成自己的信息发给B
,这里面有A
的sdp
信息,webRtc
中这个过程叫应答answer
。 - 创建的
webRtc
连接的时候会使用一个监听器,能监听自己的candidate
候选信息有没有制作完,这里面是ice
的信息。A
跟B
都要监听,制作完后发给对方,对方再存到webRtc
连接中,到此双方连接完成。 - 当一方的媒体源改变时(关闭/打开 麦克风/摄像头/共享桌面),通知其他人连接过期,然后进行以上步骤进行重新连接(除了加入的媒体流不一样,其他一样)
代码参考
打开页面告诉其他人加入会议,这个调用的接口,后台用webSocket
发给了其他人
onMounted(async () => {
/**打开页面告诉其他人加入*/
meetingInfoApi.sentMessage({
type: 'new',
meetId: props.id,//这个是会议的id,我这是个组件,从父组件传过来的
sendId: data.userInfo.value.id,//这个是获取的登录人的id,作为唯一标识用
})
})
监听webSocket
返回,我这里用了一个对象用来存跟会议中其他人沟通的webRtc
连接,如果只是一对一,可以声明一个存连接的变量就行
这个是声明的变量
const cameraVideo = ref(null);//video标签的ref引用
const connectList = ref({
}),//用来存跟其他人连接的rtc连接
const mediaStream = ref(),//用来存媒体信息
const usersList= ref(),//用来存其他用户信息
工具方法,看connectList
中有没有请求连接人的专属连接,没有就创建一个
/**有用户请求连接,生成对应的本地连接保存下来,下次直接用*/
getConnection(userId) {
let connection = data.connectList.value?.[userId];
if