一、什么是WebRTC
WebRTC(Web Real-Time Communication)是一个由Google、Mozilla、Opera等公司发起的开源项目,它支持网页浏览器进行实时音视频对话。它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和音频流或者其他任意数据的传输。对于开发者而言,WebRTC提供了一套W3C Javascript API,包括音视频的采集、编解码、网络传输、显示等功能,使得开发者能够快速构建出音视频应用。WebRTC标准在较高层面上涵盖了两种不同的技术:媒体捕获和点对点连接。
二、媒体捕获
媒体捕获设备包括摄像头和麦克风,还包括屏幕捕获设备。可以利用navigator
这个浏览器内置对象身上的API来获取媒体流。
navigator.mediaDevices.getUserMedia()
:获取摄像头和麦克风媒体流navigator.mediaDevices.getDisplayMedia()
:获取屏幕录制的媒体流
API的具体使用可以参考 MDN。从技术手册可以知道,getUserMedia()
方法需要传入一个constraints
约束对象作为参数。该参数用来指定请求的媒体类型和相对应的参数,而且必须指定至少一个类型。媒体类型具体可以配置哪些属性,可以使用navigator.mediaDevices.getSupportedConstraints()
来获取。
2.1 获取媒体流
// 指定约束对象,获取音频流和视频流,并设置视频流的分辨率为 1280 x 720
const constraints = {
audio: true,
video: {
with: 1280,
height: 720,
},
};
// 获取媒体流
const stream = navigator.mediaDevices.getUserMedia(constraints);
console.log(stream);
运行上面的代码,浏览器会询问是否打开摄像头和麦克风,设置为允许就能获取媒体流了。
打开控制台,发现getUserMedia()
方法返回的是一个Promise。
2.2 展示画面
将获取到的媒体流数据,绑定到一个video
标签的srcObject
属性上,就能通过video
标签看到视频画面了。
这里有两个地方需要注意:
getUserMedia()
方法返回的是Promise,所以需要在then方法中去绑定video
标签的srcObject
属性,或者使用async await
的方式去获取Promise的值video
标签需要设置autoplay
属性,否则视频没办法自动播放
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webrtc</title>
<style>
.play-content {
width: 300px;
height: 300px;
}
</style>
</head>
<body>
<video class="play-content" autoplay></video>
<script src="./webrtc01.js"></script>
</body>
</html>
// 指定约束对象,获取音频流和视频流,并设置视频流的分辨率为 1280 x 720
const constraints = {
audio: true,
video: {
with: 1280,
height: 720,
},
};
// 获取媒体流
navigator.mediaDevices.getUserMedia(constraints).then(value => {
// 将媒体流与video标签绑定
document.querySelector(".play-content").srcObject = value
})
执行上面的代码,就能看到视频画面了
2.3 捕获屏幕
上面使用了getUserMedia()
这个API来获取媒体流。接下来使用getDisplayMedia()
这个API来捕获屏幕。使用方法和getUserMedia()
一致
// 指定约束对象,获取音频流和视频流,并设置视频流的分辨率为 1280 x 720
const constraints = {
audio: true,
video: {
with: 1280,
height: 720,
},
};
// 获取屏幕的媒体流,并与video标签绑定
navigator.mediaDevices.getDisplayMedia(constraints).then((value) => {
document.querySelector(".play-content").srcObject = value;
});
运行上面的代码,浏览器会让你选择一个要打开的窗口。随便选择一个,就能获取到媒体流了。
三、点对点连接
通过getUserMedia()
和getDisplayMedia()
这两个API已经可以获取到媒体流了,那现在就需要考虑如何将媒体流发送给远端的浏览器。WebRTC要建立点对点的连接,首先有两个关键的问题要解决:
-
媒体协商:
媒体协商主要关注通信双方支持的编解码器及其参数。这包括音频参数(如采样率、采样大小、通道数)和视频参数(如分辨率、帧率等)。媒体协商的目的是确保双方能够交换和理解彼此的音视频流。这通常通过Session Description Protocol(SDP)进行。
-
网络协商:
网络协商则主要关注在互联网上建立可通信的链路。由于大多数设备没有独立的公网IP,因此需要进行网络地址转换(NAT)。WebRTC使用STUN(Session Traversal Utilities for NAT)和TURN(Traversal Using Relays around NAT)协议来帮助完成这一操作。STUN协议用于发现公共IP地址和UDP端口号,而TURN协议则用于在直接通信不可行时提供中继服务。网络协商的目的是找到一条可靠的网络路径,使得通信双方能够建立连接并进行实时通信。
也就是说,要确保WebRTC成功通信,需要通过媒体协商来确定双方都可用的编解码方式,还需要通过网络协商确定两端可建立正确通信链路的Ip
地址。下面是WebRTC建立通信的图解,针对里面的过程进行逐一讲解
3.1 RTCPeerConnection
具体介绍查看MDN
RTCPeerConnection是WebRTC(Web实时通信)API的一部分,它用于在浏览器之间建立点对点(peer-to-peer)连接,以支持实时通信,如音频、视频和数据交换。RTCPeerConnection提供了创建和管理这些连接所需的底层机制。
根据上面的图解,可以知道WebRTC建立通信的流程大致有以下过程:
- 客户端1创建RTCPeerConnection对象,并将本地媒体流通过addTrack方法添加到对象身上
- 通过createOffer方法创建本地SDP描述,并通过RTCPeerConnection对象的setLocalDescription方法设置本地描述
- 将SDP描述信息通过信令服务器转发给客户端2
- 客户端2收到发来的SDP描述,创建RTCPeerConnection对象,并通过
setRemoteDescription
方法设置远端描述 - 客户端2通过
createAnswer
方法创建本地的SDP描述,并通过 setLocalDescription方法设置本地描述 - 客户端2将
Answer SDP
通过信令服务器转发给客户端1 - 客户端1收到发来的
Answer SDP
,通过setRemoteDescription
方法设置远端描述
下面根据以上流程,创建一个WebRtcClient
类。该类具有绑定本地媒体流、创建offer、创建answer、接收answer等方法。
在获取本地流时,我获取的是屏幕捕获的流,这样方便通过两个不一样的窗口看到效果。
class WebRtcClient {
// 初始化 RTCPeerConnection 对象
constructor() {
this.pc = new RTCPeerConnection();
this.offer = "";
this.answer = "";
}
// 获取本地媒体流,并绑定到本地 video标签
async init() {
const localVideoBox = document.querySelector(".local");
const remoteVideoBox = document.querySelector(".remote");
// 设置约束
const constraints = {
audio: true,
video: true,
};
// 获取视频流 (切换选择使用摄像头还是屏幕)
// const stream = await navigator.mediaDevices.getUserMedia(constraints);
const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
localVideoBox.srcObject = stream;
// 将本地媒体流轨道添加到 RTCPeerConnection对象 中
stream.getTracks().forEach((track) => {
this.pc.addTrack(track, stream);
});
// 监听远程流变化
this.pc.ontrack = (e) => {
console.log("远端流变化了", e);
remoteVideoBox.srcObject = e.streams[0];
};
}
// 创建offer
async createOffer() {
// 存在新的候选,需要更新SDP信息
this.pc.onicecandidate = async (e) => {
if (e.candidate) {
this.offer = this.pc.localDescription;
}
};
// 创建offer, 并设置为本地SDP描述
const offer = await this.pc.createOffer();
await this.pc.setLocalDescription(offer);
}
// 创建answer
async createAnswer(offer) {
// 存在新的候选,需要更新SDP信息
this.pc.onicecandidate = async (e) => {
if (e.candidate) {
this.answer = this.pc.localDescription;
}
};
// 收到对端发送的offerSDP,设置为远端SDP
await this.pc.setRemoteDescription(offer);
// 创建answer,设置为本地SDP
const answer = await this.pc.createAnswer();
await this.pc.setLocalDescription(answer);
}
// 添加answerSDP
async addAnswer(answer) {
await this.pc.setRemoteDescription(answer);
}
}
const client = new WebRtcClient();
client.init();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.local,
.remote {
width: 300px;
height: 300px;
}
</style>
</head>
<body>
<video class="local" autoplay></video>
<video class="remote" autoplay></video>
<script src="./webrtcClient.js"></script>
</body>
</html>
下面一步一步操作,实现通信
-
打开两个浏览器页面,分别共享不同的画面
-
左边浏览器中调用client对象的createOffer方法
-
复制左边client对象的offerSDP,调用右边client对象的createAnswer方法,传入offerSDP
直接右键复制,实际应该使用信令服务器进行转发
调用右边client对象的createAnswer方法,传入offerSDP
-
复制右边client对象的answerSDP,调用左边client对象的addAnswer方法,并传入answerSDP
然后,两个浏览器页面就可以进行视频通话了
上面的SDP交换过程,是手动进行的,主要是为了了解整个通信流程。后面可以创建一个信令服务器,通过WebSocket实现SDP的交换。
标签:SDP,媒体,获取,基础,pc,使用,const,WebRTC From: https://www.cnblogs.com/finish/p/18085795