基于WebRTC实现音视频通话

ops/2025/1/16 3:35:57/

客户端采用 WebRTC 技术(推流),通讯用 websocket。

WebRTC 像是一个面试过程:
第一步:发起方(拨打电话者)点击拨打电话时,获取本地媒体流并推流给接收方同时捕获接收方推过来的流,捕获到后把流设置到 dom 上,监听 ICE 候选确保能点对连接,生成 offer,通过 websocket 告知接收方并拉起等待接听界面。

//获取媒体流
stream.value = await navigator.mediaDevices.getUserMedia({video: true,audio: true
});// 初始化 PeerConnection
peerConnection.value = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.l.google.com:19302'}]
});// 推流给接收方
stream.value.getTracks().forEach((track) => {peerConnection.value.addTrack(track, stream.value);
});// 捕获接收方的流
peerConnection.value.ontrack = (event) => {remoteStream.value = event.streams[0];if (callType.value === TypeVideo) {remoteVideo.value.srcObject = remoteStream.value;} else {remoteAudio.value.srcObject = remoteStream.value;}
};// 监听ICE候选,确保 WebRTC 的点对点连接能够成功建立
peerConnection.value.onicecandidate = (event) => {if (event.candidate) {//发送candidatews.send(event.candidate);}
};
// 创建 offer
const offer = await peerConnection.value.createOffer();
await peerConnection.value.setLocalDescription(offer);//发送offer,这里发送的offer可以理解成是接收方用来捕获发起方流的一个凭证,接收方通过peerConnection.value.ontrack可以捕获到。
ws.send(offer);
//拉起等待接听界面
showCall.value = true;
//状态等待接听
callStatus.value = 'wating';

第二步:接收方收到 offer 后,第一步是拉起来电界面,第二步是选择接听或者挂断。
1)拉起来电接听界面

//拉起来电接听界面
showCall.vue = true;
//状态来电接听
callStatus.value = 'coming';
//初始化来电人信息等
....

2)挂断,就是告诉发起方我挂断了,发起方就把 RTC 关掉、停止推流,dom 置空就好了

//接收方
showCall.value = false;
callStatus.value = 'closing';
ws.send('reject');//发起方
if (peerConnection.value) {peerConnection.value.close();peerConnection.value = null;
}
if (stream.value) {const tracks = stream.value.getTracks();tracks.forEach((track) => track.stop());
}
if (localVideo.value)localVideo.value.srcObject = null;
if (remoteVideo.value)remoteVideo.value.srcObject = null;
if (remoteAudio.value)remoteAudio.value.srcObject = null;
showCall.value = false;
callStatus.value = 'closing';

3)接听,操作跟拨打流程差不多,需要设置远端 SDP(发起方的 offer),添加 ICE 候选(发起方的 ice,这里需要注意的是只有远端 SDP 初始化完毕状态下才能设置 ice)

// 获取本地媒体流
...同发起方
// 初始化 PeerConnection
...同发起方
// 推流给发起方
...同发起方
// 捕获发起方的流
...同发起方
// 监听ICE候选
...同发起方//设置远端SDP
await peerConnection.value.setRemoteDescription(new RTCSessionDescription(caller.value.offer));// 添加发起方发过来的ice
iceCandidateQueue.value.forEach(async (candidate) => {
await  peerConnection.value.addIceCandidate(candidate);
});
iceCandidateQueue.value  = [];// 创建 answer
const  answer  =  await  peerConnection.value.createAnswer();
await  peerConnection.value.setLocalDescription(answer);//发送answer给发起方
ws.send(answer);
//状态通话中
callStatus.value = 'calling';

关于 ice 的处理,就是远端 SDP 初始化完毕状态可以直接设置,未初始化完毕就存到 iceCandidateQueue 队列备用

// 处理新的 ICE 候选
const handleNewICECandidate = async (candidate) => {const iceCandidate = new RTCIceCandidate(candidate);if (peerConnection.value?.signalingState === 'have-remote-offer' || peerConnection.value?.signalingState === 'stable') {peerConnection.value.addIceCandidate(iceCandidate);} else {iceCandidateQueue.value.push(iceCandidate);}
};

最后一步:发起方收到接收方的答复(接收方接听了),设置远端 SDP(接收方的 answer), 设置 ICE(接受方的 ice)

//设置远端SDP
await peerConnection.value.setRemoteDescription(new RTCSessionDescription(caller.value.answer));//添加ICE
iceCandidateQueue.value.forEach(async (candidate) => {await peerConnection.value.addIceCandidate(candidate);
});
iceCandidateQueue.value = [];
//状态接听中
callStatus.value = 'calling';

这就是 WebRTC 视频通话的关键代码跟流程!

例图:


http://www.ppmy.cn/ops/150454.html

相关文章

计算机网络之---SSL/TLS协议

SSL/TLS协议 **SSL(Secure Sockets Layer)和TLS(Transport Layer Security)**是加密协议,用于确保通过不安全的网络(如互联网)传输的数据的安全性和隐私。它们通过提供数据加密、身份验证和数据…

jsx语法中el-table-v2中cellRender如何使用动态绑定

答案::attribute"xx"改为attribute{xx} 改写: const columns ref([{ key: index, dataKey: index, title: t(setting.index), width: 100 },{ key: no, dataKey: no, title: t(setting.key), width: 100 },{ key: name, dataKey: name, tit…

【update 更新数据语法合集】.NET开源ORM框架 SqlSugar 系列

系列文章目录 🎀🎀🎀 .NET开源 ORM 框架 SqlSugar 系列 🎀🎀🎀 文章目录 系列文章目录前言 🍃一、实体对象更新1.1 单条与批量1.2 不更新某列1.3 只更新某列1.4 NULL列不更新1.5 无主键/指定列…

机器学习 - 如何选择函数集合?

一、关于机器学习的基本过程 对一个预测任务,输入特征向量为 𝒙,输出标签为 𝑦,我们选择一个函数集合 F,通过学习算法 𝒜 和一组训练样本 𝒟,从 F 中学习到函数 &#x…

C#中 string.Equals 和 == 区别

在 C# 中,string.Equals方法和运算符都可用于比较字符串,但它们在某些方面存在区别,主要如下: **string.Equals方法:**是一个实例方法,用于比较两个字符串对象的内容是否相等,比较时会根据字符…

phpstorm jetbrain 配置review code

禁用Unused CSS selector 步骤: 在 PhpStorm 中,点击顶部菜单 File > Settings(Windows/Linux)或 PhpStorm > Preferences(macOS)。在 Inspections 界面左侧的搜索框中输入 CSS。展开 CSS 部分&…

算法-贪心算法简单介绍

下面是贪心算法视频课的导学内容. 目录 1. 什么是贪心算法?2. 贪心算法简单的三个例子:1. 找零问题2. 最小路径和问题3. 背包问题 3. 贪心算法的特点4. 贪心算法学习的方式? 1. 什么是贪心算法? 简单来说, 我们称以局部最优进而使得全局最优的一种思想实现出来的算法为贪心…

从零开始开发纯血鸿蒙应用之多签名证书管理

从零开始开发纯血鸿蒙应用 一、前言二、鸿蒙应用配置签名证书的方式1、自动获取签名证书2、手动配置签名证书 三、多签名证书配置和使用四、多证书使用 一、前言 由于手机操作系统,比电脑操作系统脆弱很多,同时,由于手机的便携性&#xff0c…