WebRTC 基础

server/2024/12/15 23:42:24/

WebRTC 基础

目录

  1. 什么是 WebRTC
  2. WebRTC 的基本概念
  3. WebRTC 的基本流程
    • 连接建立流程图
  4. WebRTC 的基本对象
    • RTCPeerConnection
    • RTCSessionDescription
    • RTCIceCandidate
  5. WebRTC API 详解
    • RTCPeerConnection API
    • 媒体流 API
  6. 详细的代码示例
    • 基本连接示例
    • 完整的 WebRTC 实现示例
  7. 总结

什么是 WebRTC

WebRTC(Web Real-Time Communication)是一项技术,它使浏览器和移动应用程序能够通过简单的 API 直接进行点对点(Peer-to-Peer,P2P)的音频、视频和数据传输,而无需借助中间服务器。WebRTC 主要用于以下场景:

  • 视频通话:在浏览器之间建立实时的视频聊天功能。
  • 音频通话:支持高质量的语音通话。
  • 数据传输:用于传输任意数据,如文件、消息等。
  • 实时协作:在多个客户端之间同步文档、白板等应用。

WebRTC 具有以下几个关键特点:

  • 实时性:提供低延迟的音视频通信,适用于需要即时反馈的场景。
  • 跨平台:支持在不同设备和浏览器间进行通信,广泛适用于桌面和移动端。
  • 安全性:通过 SRTP(Secure Real-time Transport Protocol)对传输的数据进行加密,确保通信的安全性。
  • 开源性:WebRTC 是一个开源项目,开发者可以自由使用和修改。

WebRTC 的基本概念

在深入了解 WebRTC 的使用之前,理解以下几个关键概念至关重要:

1. 媒体流(MediaStream)

媒体流表示音频或视频流,是由多个媒体轨道(MediaTrack)组成的。每个轨道可以是音频轨道或视频轨道。WebRTC 通过 getUserMedia() API 获取媒体流并将其传输给对等端。

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {// 使用获取到的媒体流}).catch(error => {console.error('Error accessing media devices.', error);});
  • 关于媒体流,可以参考:MediaStream 的媒体流对象 (stream) 和流媒体轨道 (track) 详解
2. 信令(Signaling)

信令是指在两个对等端之间交换元数据的过程。元数据包括:

  • SDP(Session Description Protocol):描述会话的格式信息,如媒体类型、编解码器、网络参数等。
  • ICE 候选(ICE Candidates):用于 P2P 连接的网络候选信息。

WebRTC 并不规定如何实现信令,通常由开发者通过 WebSocketXHR 或其他协议来完成。完成信令交换相关操作的服务称作信令服务器

3. 会话描述协议(SDP)

SDP 是一种文本格式,用于描述多媒体通信会话。它包含有关媒体格式、编解码器、带宽、网络等信息。SDP 是 WebRTC 连接建立过程中的关键要素之一。

4. ICE(Interactive Connectivity Establishment)

ICE 是一个用于帮助 P2P 连接在不同网络环境下建立的框架。ICE 会尝试使用各种网络候选(candidate),包括本地、STUN 和 TURN 服务器提供的候选,以确保 P2P 连接的建立。

  • STUN(Session Traversal Utilities for NAT):帮助客户端发现其外部网络地址。
  • TURN(Traversal Using Relays around NAT):用于在 P2P 连接无法直接建立时,通过中继服务器转发流量。
上面的过程强烈建议参考这位作者的文章:
  • 一文详解 WebRTC 基础
5. 数据通道(DataChannel)

数据通道 是 WebRTC 提供的一种机制,用于在对等端之间传输任意数据,如文本、文件等。它通过 RTCDataChannel 对象来创建,具有低延迟和高吞吐量的特点。

const dataChannel = pc.createDataChannel("myDataChannel");// 监听消息接收事件
dataChannel.onmessage = (event) => {console.log("Received Message:", event.data);
};// 发送消息
dataChannel.send("Hello, WebRTC!");

WebRTC 的基本流程

WebRTC 的连接建立过程分为以下几个步骤:

1. 获取本地媒体流

使用 getUserMedia() API 获取本地的音频和视频流,并将其添加到 RTCPeerConnection 中。

2. 创建 RTCPeerConnection 对象

实例化 RTCPeerConnection 对象,并设置 ICE 服务器配置。RTCPeerConnection 是 WebRTC 的核心对象,用于管理和控制 P2P 连接。

3. 创建 Offer 并设置本地描述

调用 createOffer() 创建一个会话描述(SDP),并使用 setLocalDescription() 将其设置为本地描述。

4. 通过信令通道发送 Offer

将生成的 SDP 通过信令通道发送给远端。

5. 远端接收 Offer 并生成 Answer

远端使用 setRemoteDescription() 接收 SDP,并调用 createAnswer() 生成一个 Answer,然后通过信令通道发送回给本地。

6. 本地接收 Answer 并设置远程描述

本地使用 setRemoteDescription() 接收远端的 Answer,并将其设置为远程描述。

7. 交换 ICE 候选

通过 onicecandidate 事件监听 ICE 候选的生成,并通过信令通道交换候选信息,确保 P2P 连接的建立。

8. 连接建立并开始传输数据

当双方都交换完 SDP 和 ICE 候选后,P2P 连接建立,可以开始音视频和数据的传输。

连接建立流程图

#mermaid-svg-MVIa2sSazDRVuKar {font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MVIa2sSazDRVuKar .error-icon{fill:#552222;}#mermaid-svg-MVIa2sSazDRVuKar .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MVIa2sSazDRVuKar .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-MVIa2sSazDRVuKar .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MVIa2sSazDRVuKar .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MVIa2sSazDRVuKar .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MVIa2sSazDRVuKar .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MVIa2sSazDRVuKar .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MVIa2sSazDRVuKar .marker.cross{stroke:#333333;}#mermaid-svg-MVIa2sSazDRVuKar svg{font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MVIa2sSazDRVuKar .label{font-family:“trebuchet ms”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-MVIa2sSazDRVuKar .cluster-label text{fill:#333;}#mermaid-svg-MVIa2sSazDRVuKar .cluster-label span{color:#333;}#mermaid-svg-MVIa2sSazDRVuKar .label text,#mermaid-svg-MVIa2sSazDRVuKar span{fill:#333;color:#333;}#mermaid-svg-MVIa2sSazDRVuKar .node rect,#mermaid-svg-MVIa2sSazDRVuKar .node circle,#mermaid-svg-MVIa2sSazDRVuKar .node ellipse,#mermaid-svg-MVIa2sSazDRVuKar .node polygon,#mermaid-svg-MVIa2sSazDRVuKar .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MVIa2sSazDRVuKar .node .label{text-align:center;}#mermaid-svg-MVIa2sSazDRVuKar .node.clickable{cursor:pointer;}#mermaid-svg-MVIa2sSazDRVuKar .arrowheadPath{fill:#333333;}#mermaid-svg-MVIa2sSazDRVuKar .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MVIa2sSazDRVuKar .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MVIa2sSazDRVuKar .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-MVIa2sSazDRVuKar .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-MVIa2sSazDRVuKar .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MVIa2sSazDRVuKar .cluster text{fill:#333;}#mermaid-svg-MVIa2sSazDRVuKar .cluster span{color:#333;}#mermaid-svg-MVIa2sSazDRVuKar div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MVIa2sSazDRVuKar :root{–mermaid-font-family:“trebuchet ms”,verdana,arial,sans-serif;}

获取本地媒体流

创建 RTCPeerConnection

创建 Offer 并设置本地描述

通过信令通道发送 Offer

远端接收 Offer 并生成 Answer

本地接收 Answer 并设置远程描述

交换 ICE 候选

连接建立并开始传输数据

信令交换流程图

#mermaid-svg-H1fruSSJV9h8FFHl {font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-H1fruSSJV9h8FFHl .error-icon{fill:#552222;}#mermaid-svg-H1fruSSJV9h8FFHl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-H1fruSSJV9h8FFHl .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-H1fruSSJV9h8FFHl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-H1fruSSJV9h8FFHl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-H1fruSSJV9h8FFHl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-H1fruSSJV9h8FFHl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-H1fruSSJV9h8FFHl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-H1fruSSJV9h8FFHl .marker.cross{stroke:#333333;}#mermaid-svg-H1fruSSJV9h8FFHl svg{font-family:“trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-H1fruSSJV9h8FFHl .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H1fruSSJV9h8FFHl text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-H1fruSSJV9h8FFHl .actor-line{stroke:grey;}#mermaid-svg-H1fruSSJV9h8FFHl .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-H1fruSSJV9h8FFHl .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-H1fruSSJV9h8FFHl #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-H1fruSSJV9h8FFHl .sequenceNumber{fill:white;}#mermaid-svg-H1fruSSJV9h8FFHl #sequencenumber{fill:#333;}#mermaid-svg-H1fruSSJV9h8FFHl #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-H1fruSSJV9h8FFHl .messageText{fill:#333;stroke:#333;}#mermaid-svg-H1fruSSJV9h8FFHl .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H1fruSSJV9h8FFHl .labelText,#mermaid-svg-H1fruSSJV9h8FFHl .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-H1fruSSJV9h8FFHl .loopText,#mermaid-svg-H1fruSSJV9h8FFHl .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-H1fruSSJV9h8FFHl .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-H1fruSSJV9h8FFHl .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-H1fruSSJV9h8FFHl .noteText,#mermaid-svg-H1fruSSJV9h8FFHl .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-H1fruSSJV9h8FFHl .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H1fruSSJV9h8FFHl .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H1fruSSJV9h8FFHl .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H1fruSSJV9h8FFHl .actorPopupMenu{position:absolute;}#mermaid-svg-H1fruSSJV9h8FFHl .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-H1fruSSJV9h8FFHl .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H1fruSSJV9h8FFHl .actor-man circle,#mermaid-svg-H1fruSSJV9h8FFHl line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-H1fruSSJV9h8FFHl :root{–mermaid-font-family:“trebuchet ms”,verdana,arial,sans-serif;} Offerer Signaling Server Answerer Send SDP Offer Forward SDP Offer Send SDP Answer Forward SDP Answer Set Remote Description Set Local Description Offerer Signaling Server Answerer

WebRTC 的基本对象

RTCPeerConnection
  • 可参考:RTCPeerConnection API 详细介绍(含RTCDataChannel)

RTCPeerConnection 是 WebRTC 的核心对象,用于管理 P2P 连接。它提供了以下主要功能:

  • 创建和维护 P2P 连接
  • 处理 ICE 候选
  • 发送和接收媒体流
构造函数
const pc = new RTCPeerConnection(configuration);
  • configuration:包含 ICE 服务器配置的对象。可以设置 STUN 和 TURN 服务器。
    • iceServers: 一个包含 STUN 和 TURN 服务器地址的数组,例如:

      const configuration = {iceServers: [{ urls: 'stun:stun.l.google.com:19302' },{ urls: 'turn:your-turn-server.com', username: 'user', credential: 'pass' }]
      };
      
主要方法和属性
  • createOffer(options):创建一个 SDP Offer,用于发起连接。
    • options: 可选参数,用于指定创建 Offer 的约束条件,如是否仅包含视频轨道等。
  • createAnswer(options):创建一个 SDP Answer,用于回应对方的 Offer。
    • options: 可选参数,用于指定创建 Answer 的约束条件。
  • setLocalDescription(description):设置本地 SDP 描述。
    • description: 一个 RTCSessionDescription 对象,通常是由 createOffercreateAnswer 方法生成的。
  • setRemoteDescription(description):设置远程 SDP 描述。
    • description: 一个 RTCSessionDescription 对象,表示远端的 SDP 描述。
  • addIceCandidate(candidate):向连接中添加一个 ICE 候选。
    • candidate: 一个 RTCIceCandidate 对象,表示新的 ICE 候选。
事件处理
  • onicecandidate:当新的 ICE 候选生成时触发,用于向远端发送 ICE 候选。

    pc.onicecandidate = (event) => {if (event.candidate) {// 发送候选信息给远端}
    };
    
  • ontrack:当新的媒体轨道(音频或视频)添加到连接时触发。

    pc.ontrack = (event) => {const remoteStream = event.streams[0];// 将远程媒体流添加到视频元素
    };
    
  • ondatachannel:当远程对等端创建一个数据通道时触发。

    pc.ondatachannel = (event) => {const receiveChannel = event.channel;receiveChannel.onmessage = (e) => {console.log("Data Channel Message:", e.data);};
    };
    
RTCSessionDescription

RTCSessionDescription 对象用于表示 WebRTC 连接的 SDP 信息。它包含连接的媒体类型、编解码器和网络配置等信息。

构造函数
const description = new RTCSessionDescription({ type, sdp });
  • type:描述的类型,可以是 "offer""answer""rollback"
  • sdp:包含 SDP 的字符串。
示例
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);const description = new RTCSessionDescription({ type: "offer", sdp: offer.sdp });
RTCIceCandidate

RTCIceCandidate 对象表示 WebRTC 的 ICE 候选信息。它用于描述潜在的 P2P 连接路径。

构造函数
const candidate = new RTCIceCandidate({ candidate, sdpMid, sdpMLineIndex });
  • candidate:表示候选的网络地址。
  • sdpMid:与候选相关的媒体流标识符。
  • sdpMLineIndex:与候选相关的媒体流索引。
示例
pc.addIceCandidate(new RTCIceCandidate(candidate));

示例
const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
const pc = new RTCPeerConnection(configuration);// 添加媒体轨道
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {stream.getTracks().forEach(track => pc.addTrack(track, stream));});// 处理 ICE 候选
pc.onicecandidate = (event) => {if (event.candidate) {// 发送 ICE 候选给远端}
};// 处理远端媒体流
pc.ontrack = (event) => {const remoteStream = event.streams[0];document.querySelector("#remoteVideo").srcObject = remoteStream;
};// 创建数据通道
const dataChannel = pc.createDataChannel("chat");dataChannel.onmessage = (event) => {console.log("Received Message:", event.data);
};pc.createOffer().then(offer => {return pc.setLocalDescription(offer);
}).then(() => {// 发送 offer 到远端
}).catch(console.error);

详细的代码示例

基本连接示例

以下是一个简单的 WebRTC 连接示例,展示了如何建立一个基本的音视频连接。

// 创建 RTCPeerConnection 对象
const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
const pc = new RTCPeerConnection(configuration);// 获取本地媒体流
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {document.querySelector("#localVideo").srcObject = stream;stream.getTracks().forEach(track => pc.addTrack(track, stream));});// 处理 ICE 候选
pc.onicecandidate = (event) => {if (event.candidate) {// 发送 ICE 候选给远端}
};// 处理远端媒体流
pc.ontrack = (event) => {const remoteStream = event.streams[0];document.querySelector("#remoteVideo").srcObject = remoteStream;
};// 通过信令通道发送和接收 Offer/Answer
pc.createOffer().then(offer => {return pc.setLocalDescription(offer);
}).then(() => {// 发送 offer 到远端
}).catch(console.error);// 处理远端的 Answer
// pc.setRemoteDescription(new RTCSessionDescription(answer));
完整的 WebRTC 实现示例

以下是一个更复杂的完整 WebRTC 示例,涵盖了音视频连接、数据通道和错误处理。

const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
const pc = new RTCPeerConnection(configuration);// 获取本地媒体流
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {document.querySelector("#localVideo").srcObject = stream;stream.getTracks().forEach(track => pc.addTrack(track, stream));}).catch(error => {console.error("Error accessing media devices.", error);});// 处理 ICE 候选
pc.onicecandidate = (event) => {if (event.candidate) {// 发送 ICE 候选给远端}
};// 处理远端媒体流
pc.ontrack = (event) => {const remoteStream = event.streams[0];document.querySelector("#remoteVideo").srcObject = remoteStream;
};// 创建数据通道
const dataChannel = pc.createDataChannel("chat");
dataChannel.onopen = () => console.log("Data channel is open");
dataChannel.onmessage = (event) => console.log("Received message:", event.data);
dataChannel.onclose = () => console.log("Data channel is closed");// 创建 Offer 并设置本地描述
pc.createOffer().then(offer => {return pc.setLocalDescription(offer);
}).then(() => {// 通过信令通道发送 Offer
}).catch(console.error);// 处理远端的 Answer
// pc.setRemoteDescription(new RTCSessionDescription(answer)).catch(console.error);// 添加错误处理
pc.oniceconnectionstatechange = () => {if (pc.iceConnectionState === "failed") {console.error("ICE connection failed.");}
};

总结

WebRTC 为实时通信提供了强大的功能,能够在不依赖中介服务器的情况下,建立跨平台的 P2P 连接。本文介绍了WebRTC 基本概念和相关流程,下文将主要介绍 WebRTC 创建端与接收端的代码实现。


参考来源

  • 一文详解 WebRTC 基础

http://www.ppmy.cn/server/150473.html

相关文章

在C#中编程绘制和移动线段

这个示例允许用户绘制和移动线段。它允许您根据鼠标下方的内容执行三种不同的操作。 当鼠标位于某个线段上时,光标会变成手的形状。然后您可以单击并拖动来移动该线段。当鼠标位于线段的终点上时,光标会变成箭头。然后您可以单击并拖动以移动终点。当鼠…

前端面试题目 (Node.JS-Express框架)[二]

在 Express 中如何使用 Passport.js 进行身份认证? Passport.js 是一个 Node.js 的身份验证中间件,它可以很容易地与 Express 集成。下面是一个简单的示例,展示了如何使用 Passport.js 进行基本的身份认证。 安装依赖 npm install express passport …

SSM 架构下的垃圾分类系统,引领环保变革

摘 要 随着现在网络的快速发展,网上管理系统也逐渐快速发展起来,网上管理模式很快融入到了许多国有企业的之中,随之就产生了“垃圾分类系统”,这样就让垃圾分类系统更加方便简单。 对于本垃圾分类系统的设计来说,系统…

网络术语MSS/MTU/TSO/Len说明

在网络通信中,MSS、MTU、Len 和 TSO 是与数据包传输相关的关键概念。以下是对这些概念的详细解释和它们之间的关系。 1. MSS(Maximum Segment Size) 1.1 定义 MSS 是 TCP 协议中的一个参数,表示 TCP 数据段中可以携带的最大数…

NLP-Huggingface基本使用方法

NLP的网络结构大同小异,只不过训练策略可能会不同。因为与图像cv不同,文本训练数据非常的多,cv可以使用10几张就可以获得特征向量,而文本做不到学几句话就能让计算机听得懂话。因此,我们都需要使用预训练模型&#xff…

【HarmonyOS】鸿蒙应用实现手机摇一摇功能

【HarmonyOS】鸿蒙应用实现手机摇一摇功能 一、前言 手机摇一摇功能,是通过获取手机设备,加速度传感器接口,获取其中的数值,进行逻辑判断实现的功能。 在鸿蒙中手机设备传感器ohos.sensor (传感器)的系统API监听有以下&#xf…

EasyExcel设置表头上面的那种大标题(前端传递来的大标题)

1、首先得先引用easyExcel的版本依赖&#xff0c;我那 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version> </dependency> 2、然后得弄直接的实体类&#xff0c;&…

Qt编写的文件传输工具

使用QT编写的文件传输工具 文件传输工具通过发送udp广播消息将IP广播给其他开启该程序的局域网机器 文件传输工具 通过发送udp广播消息将IP广播给其他开启该程序的局域网机器 收到的广播消息可以显示在IP地址列表中&#xff0c;点击IP地址可以自动填充到IP地址栏内 选择文件…