引言 在当今数字化的世界中,实时通信变得越来越重要。从视频会议到在线游戏,从远程医疗到物联网设备,人们希望能够立即与其他人或设备进行交流和互动。WebRTC(Web Real-Time Communication)
技术的出现填补了这一需求,为Web应用程序提供了强大的实时通信能力。本文将深入探讨 WebRTC
技术的原理、应用场景以及实现细节,带您了解这一引人注目的技术。
什么是WebRTC? WebRTC
是一种开放标准的实时通信技术,允许浏览器之间进行点对点的音频、视频和数据传输,而无需任何插件或附加软件。它是由Google、Mozilla和Opera等公司发起的一个开放源代码项目,旨在为Web应用程序提供实时通信的能力。
WebRTC的核心特性
实时性WebRTC
提供了实时的音频和视频传输能力,使用户可以在几乎没有延迟的情况下进行实时通信。这种实时性对于视频会议、在线教育和远程医疗等应用非常重要。
安全性WebRTC
通过使用加密技术来保护通信内容的安全性。它使用 DTLS(Datagram Transport Layer Security)
协议来保护数据传输的隐私,并使用 SRTP(Secure Real-time Transport Protocol)
协议来加密音频和视频数据。
去中心化WebRTC
采用了点对点(P2P
)通信模型,即浏览器之间直接进行通信,而不需要经过中间服务器。这种去中心化的通信方式可以提高通信的效率,并减少了对服务器资源的依赖。
跨平台WebRTC
可以在各种平台上运行,包括桌面浏览器、移动浏览器和原生应用程序。这使得开发人员可以轻松地创建跨平台的实时通信应用程序。
名词和方法解释 RTCPeerConnection RTCPeerConnection
是 WebRTC API
中的一个关键接口,用于在两个对等端之间建立点对点连接。它提供了一种实现浏览器之间实时音视频通信的方式,允许在不同浏览器之间直接传输数据,而无需通过服务器。通过 RTCPeerConnection
,用户可以在浏览器中创建一个实时通信的会话,并在其中发送和接收音频、视频或其他任何类型的数据。
作用 RTCPeerConnection
的主要作用包括:
建立和管理对等连接:RTCPeerConnection
提供了一种方法来建立和管理两个浏览器之间的点对点连接,使它们可以直接通信,无需通过中间服务器。
处理媒体流:它允许用户将本地音频、视频或数据流发送到远程对等端,并从远程对等端接收相应的媒体流。
网络协商和协议处理:RTCPeerConnection
处理与 ICE
、SDP
等相关的网络协商和协议处理,以确保对等连接的正确建立和维护。
实现信号传输和媒体传输:它还负责在对等连接之间传输信令消息和媒体数据,以确保通信的顺利进行。
参数 RTCPeerConnection
构造函数包含的1个参数,一个 RTCConfiguration
对象,用于指定配置参数。这个对象包含以下属性:
iceServers :一个 RTCIceServer
对象数组,用于指定 ICE
服务器信息。
urls
:ICE
服务器的 URL
或 URL
数组。
username
(可选):ICE
服务器的用户名。
credential
(可选):ICE
服务器的密码。
iceTransportPolicy (可选):一个枚举值,用于指定 ICE
传输策略,默认为 "all"
,目前支持的值有 "all"
| "relay"
。
bundlePolicy (可选):一个枚举值,用于指定 SDP
处理策略,默认为 "balanced"
,目前支持的值有 "balanced"
| "max-bundle"
| "max-compat"
。
rtcpMuxPolicy (可选):一个枚举值,用于指定 RTCP
复用策略,默认为 "require"
,目前支持的值有 "require"
。
peerIdentity (可选):PEER
身份验证,默认为 null。
certificates (可选):一个 RTCCertificate
对象数组,用于指定本地证书数组。
iceCandidatePoolSize (可选):一个数值,指定 ICE
候选地址池的大小。
属性和方法
restartIce()
restartIce()
方法用于在 WebRTC
对等连接中重新启动 ICE
连接过程。ICE(Interactive Connectivity Establishment)
是一种用于建立对等连接的网络协议,它可以帮助确定网络上的最佳路径,以确保数据能够在两个 RTCPeerConnection
之间进行有效传输。
重新启动 ICE
连接过程通常在网络连接发生变化或连接质量变差时使用,以尝试建立更稳定的连接。这可能包括重新检测网络接口、重新收集 ICE
候选项、更新 ICE Agent
状态等操作。
restartIce()
方法不接受任何参数,调用该方法将触发 ICE
连接重新启动过程。在调用此方法之后,ICE Agent
将重新开始收集候选项并尝试建立新的 ICE
连接。
下面是一个示例代码:
1 2 peerConnection.restartIce ();
setLocalDescription setLocalDescription()
方法用于将本地描述(即 SDP,Session Description Protocol)设置为 RTCPeerConnection
对象的本地描述。本地描述包含了当前对等连接的配置信息,例如媒体类型、编解码器、传输协议等。
通常情况下,当我们创建一个新的对等连接时,会先通过调用 createOffer()
或 createAnswer()
方法生成一个 SDP 描述,并将其设置为本地描述。然后,我们将本地描述发送给远程对等方,远程对等方通过调用 setRemoteDescription()
方法将其应用到对等连接中。这样,两个对等方就可以根据各自的本地描述进行协商,建立一条可靠的连接。
以下是一个简单的示例,展示了如何使用 setLocalDescription()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const peerConnection = new RTCPeerConnection ();peerConnection.createOffer () .then ((offer ) => { return peerConnection.setLocalDescription (offer); }) .then (() => { console .log ("Local description set successfully." ); }) .catch ((error ) => { console .error ("Error setting local description:" , error); });
在上面的示例中,我们首先创建了一个 SDP offer,然后调用 setLocalDescription()
方法将其设置为本地描述。成功设置本地描述后,我们会在控制台输出成功的消息。如果设置本地描述失败,则会捕获到错误并输出错误消息。
setRemoteDescription setRemoteDescription()
方法用于将远程对等连接的描述信息设置到本地对等连接中。这个描述信息通常是由远程对等方通过 SDP(Session Description Protocol)提供的,包含了远程对等方的媒体信息和网络连接信息。
在 WebRTC 中,当本地对等连接收到远程对等方发送的 SDP offer 或 answer 时,需要通过 setRemoteDescription()
方法将其设置到本地对等连接中,以便进行后续的连接协商和媒体交换。
以下是一个简单的示例,展示了如何使用 setRemoteDescription()
方法将远程对等连接的描述信息设置到本地对等连接中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const remoteDescription = { type : 'offer' , sdp : '...' }; peerConnection.setRemoteDescription (remoteDescription) .then (() => { console .log ('Remote description set successfully.' ); }) .catch ((error ) => { console .error ('Error setting remote description:' , error); });
在上面的示例中,我们假设 remoteDescription
是远程对等连接的描述信息,包含了描述的类型(这里假设为 SDP offer)和 SDP offer 的内容。然后,我们通过 setRemoteDescription()
方法将这个描述信息设置到本地对等连接中。如果设置成功,则会打印日志表示远程描述已成功设置,否则会捕获到错误并输出错误消息。
addTransceiver
addTransceiver()
方法用于向对等连接中添加新的媒体传输通道(RTCRtpTransceiver
)。它的参数包括:
trackOrKind :要添加到传输通道的媒体轨道(MediaStreamTrack
)或媒体类型(string
)。如果是 MediaStreamTrack
类型,则表示要添加的具体媒体轨道。如果是 string
类型,则表示要添加的媒体类型,如 "audio"
或 "video"
。
init (可选):一个可选的 RTCRtpTransceiverInit
对象,用于配置传输通道的初始化选项。该对象包括以下属性:
direction
:传输通道的传输方向,可以是 "sendrecv"
、"sendonly"
、"recvonly"
或 "inactive"
。
streams
:一个包含了要与传输通道关联的 MediaStream
对象的数组。
sendEncodings
:一个包含了与传输通道相关联的编码设置的数组,用于配置传输的编码参数。
该方法返回一个 RTCRtpTransceiver
对象,表示新添加的传输通道。这个传输通道可以用于控制和管理新添加的媒体流的传输。通过使用 addTransceiver()
方法,您可以动态地向对等连接中添加新的媒体轨道,从而实现更灵活和动态的媒体流管理。
事件
onnegotiationneeded
onnegotiationneeded
事件是 WebRTC
中的一个事件,它在需要重新协商(negotiation
)时触发。当需要创建或重新创建对等连接时(例如添加或删除数据流或轨道,或者更改连接配置),就会触发此事件。
在此事件触发时,通常会调用 createOffer()
或 createAnswer()
方法来生成一个新的 SDP
(会话描述协议)以进行协商。然后,这个新的 SDP
将被传递给远程 RTCPeerConnection
,以更新对等连接的配置。
以下是一个示例代码:
1 2 3 4 5 6 7 8 9 10 peerConnection.onnegotiationneeded = async () => { try { const offer = await peerConnection.createOffer (); await peerConnection.setLocalDescription (offer); sendOfferToRemotePeer (offer); } catch (error) { console .error ('Error creating offer:' , error); } };
在实际应用中,您可以根据需要在 onnegotiationneeded
事件中执行其他操作,例如创建或更新对等连接的配置,以确保连接的稳定性和可靠性。
onicecandidate onicecandidate
事件在 ICE(Interactive Connectivity Establishment)
协商过程中生成 ICE
候选时触发。ICE
候选用于发现网络路径和连接两个 RTCPeerConnection
之间的传输地址。当本地 RTCPeerConnection
发现一个新的 ICE
候选时,会触发 onicecandidate
事件,并将候选信息传递给应用程序。
通常,在收到 ICE
候选后,应用程序会将候选信息发送给远程 RTCPeerConnection
,以便远程 RTCPeerConnection
能够知道如何直接与本地 RTCPeerConnection
进行通信。
以下是一个简单的示例,展示了如何使用 onicecandidate
事件处理程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const peerConnection = new RTCPeerConnection ();peerConnection.onicecandidate = (event ) => { if (event.candidate ) { sendIceCandidateToRemotePeer (event.candidate ); } else { console .log ("All ICE candidates have been sent." ); } }; peerConnection.createOffer () .then ((offer ) => { return peerConnection.setLocalDescription (offer); }) .catch ((error ) => { console .error ("Error creating offer:" , error); });
在上面的示例中,onicecandidate
事件处理程序将在每次发现新的 ICE 候选时被触发。在处理程序中,我们检查 event.candidate
是否存在,如果存在则将候选信息发送给远程 RTCPeerConnection
。当 event.candidate
为 null
时,表示所有的 ICE 候选已经发现并发送完毕。
RTCIceCandidate RTCIceCandidate
是 WebRTC
中的一个接口,表示 ICE (Interactive Connectivity Establishment)
候选者,用于描述网络中的地址信息,以帮助建立对等连接。以下是关于 RTCIceCandidate
接口的详细解释:
RTCIceCandidate
表示一个 ICE
候选者,包含了描述网络地址的相关信息。
RTCIceCandidate
是 WebRTC
中用于建立对等连接的关键组成部分,用于确定连接双方的网络地址和传输协议。
参数 RTCIceCandidate
接口具体的参数如下所示:
candidate :描述 ICE
候选者的字符串,通常是以 SDP (Session Description Protocol)
格式表示的。该字符串包含了 ICE
候选者的网络地址、传输协议、优先级等信息。
sdpMid :候选者所对应的媒体流的标识符,用于区分不同的媒体流。
sdpMLineIndex :候选者所在的媒体流的索引,表示候选者属于媒体流描述中的哪一行。
案例 下面是一个简单的例子,展示了如何创建一个 RTCIceCandidate
对象,并将其添加到 RTCPeerConnection
中。ICE
候选者通常是通过信令服务器交换的,用于建立对等连接并进行网络地址交换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const remoteIceCandidateInfo = { candidate : 'candidate:1838085505 1 udp 2122260223 192.168.1.101 52041 typ host generation 0' , sdpMid : 'audio' , sdpMLineIndex : 0 } const iceCandidate = new RTCIceCandidate (remoteIceCandidateInfo);peerConnection.addIceCandidate (iceCandidate) .then (() => { console .log ('ICE 候选者添加成功' ); }) .catch (error => { console .error ('添加 ICE 候选者时出错:' , error); });
RTCSessionDescription RTCSessionDescription
是 WebRTC API 中的一个重要接口,用于表示会话描述协议 (SDP) 中的一部分。SDP 是一种用于描述媒体会话的文本协议,包含了媒体流的相关信息,如编解码器、媒体类型、媒体格式、网络传输协议等。
在 WebRTC 中,RTCSessionDescription
主要用于描述通信会话中的两个关键部分:
描述远程媒体的信息 :当一个端点(通常是浏览器)接收到来自远程端点(如另一个浏览器或媒体服务器)的 SDP 描述时,它会使用 RTCSessionDescription
对象来表示该描述。这个描述包含了远程端点发送的媒体流的相关信息,如编解码器、媒体类型、网络地址等。
描述本地媒体的信息 :同样地,当一个端点想要向远程端点发送媒体流时,它会创建一个 RTCSessionDescription
对象,其中包含了本地媒体流的描述信息。这个描述会随后通过信令传输给远程端点,以便它能够了解到本地端点要发送的媒体流的相关信息。
属性 RTCSessionDescription
是 WebRTC 中用于描述会话信息的对象。它有两个主要属性:
案例 举个例子,一个典型的 RTCSessionDescription
对象可以看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const sessionDescription = { type : 'offer' , sdp : 'v=0\no=- 123456789 0 IN IP4 127.0.0.1\ns=...\nt=0 0\n...' }; peerConnection.setRemoteDescription (sessionDescription) .then (() => { }) .catch (error => { });
RTCCertificate RTCCertificate
是用于配置和管理 WebRTC
连接所使用的安全证书的接口。它可以用来指定本地端点的安全凭证,以确保通信的安全性和可靠性。
RTCCertificate
接口本身并没有提供直接创建证书的方法。相反,WebRTC API
中的 RTCPeerConnection.generateCertificate()
方法用于生成新的证书。这个方法返回一个 Promise
,在解析时会返回一个新的 RTCCertificate
实例,或者在生成证书时出现错误时返回一个错误对象。
属性和方法 RTCCertificate
接口没有提供构造函数来创建新的证书,而是通过 createOffer()
、createAnswer()
等方法间接使用。它的主要属性和方法包括:
expires
:表示证书的到期时间,以 UNIX
时间戳表示。
getFingerprints()
:返回一个包含证书指纹信息的数组。
案例 以下是一个使用 RTCPeerConnection.generateCertificate()
方法生成证书的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 let stdRSACertificate = { name : "RSASSA-PKCS1-v1_5" , modulusLength : 2048 , publicExponent : new Uint8Array ([1 , 0 , 1 ]), hash : "SHA-256" , }; let stdRSAPSSCertificate = { name : 'RSA-PSS' , modulusLength : 2048 , publicExponent : new Uint8Array ([0x01 , 0x00 , 0x01 ]), hash : 'SHA-256' , saltLength : 32 }; let stdECDSACertificate = { name : "ECDSA" , namedCurve : "P-256" , }; const certificatePromise = RTCPeerConnection .generateCertificate (stdECDSACertificate);certificatePromise.then ((certificate ) => { console .log ('New certificate generated:' , certificate); }).catch ((error ) => { console .error ('Error generating certificate:' , error); });
RTCRtpTransceiver RTCRtpTransceiver
接口表示一个媒体传输通道,用于在 WebRTC
对等连接(RTCPeerConnection
)中传输媒体流。每个传输通道可以处理一个或多个媒体流的发送和接收。
RTCRtpTransceiver
接口通常由 RTCPeerConnection
对象的 addTransceiver()
方法创建,并在对等连接的创建和管理过程中使用。它提供了灵活的控制选项,使您能够更好地管理对等连接中的媒体流传输。
属性 RTCRtpTransceiver
表示 WebRTC
对等连接(RTCPeerConnection
)中的一个媒体传输通道,该对象包含的属性和方法如下所示:
mid
:传输通道的标识符(MediaStream ID),在 SDP 中用于唯一标识传输通道。
receiver
:与传输通道相关联的接收器对象(RTCReceiver
),用于接收远程媒体流。
sender
:与传输通道相关联的发送器对象(RTCSender
),用于发送本地媒体流。
direction
:传输通道的传输方向,可以是 "sendrecv"
、"sendonly"
、"recvonly"
或 "inactive"
。
currentDirection
:当前传输通道的传输方向。
stopped
:指示传输通道是否已停止的布尔值。
方法
setCodecPreferences(codecs)
:设置传输通道的编解码器偏好,codecs
是通过 RTCRtpSender.getCapabilities
和 RTCRtpReceiver.getCapabilities
静态方法获得的编解码器。
stop()
:停止传输通道,不再发送或接收媒体流。
RTCRtpSender
和 RTCRtpReceiver
是 WebRTC API 中用于发送和接收媒体流的对象。
RTCRtpSender: RTCRtpSender
接口表示 RTCPeerConnection
中的媒体发送器。它负责发送媒体流到远程对等方。
属性
track
:成员属性,代表当前与 RTCRtpSender
相关联的 MediaStreamTrack
对象。可以是音频轨道或视频轨道。
方法
getParameters()
:成员方法,返回一个 RTCRtpSendParameters
对象,该对象包含了当前发送器的参数信息,如编码器设置等。
setParameters(parameters)
:成员方法,用于设置发送器的参数,例如修改编码器设置等。参数 parameters
是一个包含新参数的 RTC RtpSendParameters
对象。
replaceTrack(newTrack)
:成员方法,用于替换当前发送器的媒体轨道。可以用于在运行时更改发送的媒体类型(例如切换摄像头)。
getCapabilities(kind)
:静态方法,用于获取特定类型(音频或视频)的编解码器能力信息。参数 kind
可以是 “audio” 或 “video”。
示例 以下是关于如何使用 getCapabilities
方法来获取编解码器能力信息的示例代码:
1 2 3 4 5 6 7 const videoCapabilities = RTCRtpSender .getCapabilities ('video' );console .log ("Video Codecs:" , videoCapabilities.codecs );const audioCapabilities = RTCRtpSender .getCapabilities ('audio' );console .log ("Audio Codecs:" , audioCapabilities.codecs );
这段代码中,我们首先使用 RTCRtpSender.getCapabilities
方法来获取视频和音频编解码器的能力信息。然后,我们打印出返回的 codecs
属性,其中包含了关于支持的编解码器的详细信息,例如编解码器的类型、编码器和解码器参数等。
RTCRtpReceiver: RTCRtpReceiver
接口表示 RTCPeerConnection
中的媒体接收器。它负责接收远程对等方发送的媒体流。
属性
track
:属性,代表当前与 RTCRtpReceiver
相关联的 MediaStreamTrack
对象。可以是音频轨道或视频轨道。
方法
getParameters()
:成员方法,返回一个 RTC RtpReceiveParameters
对象,该对象包含了当前接收器的参数信息,如解码器设置等。
getContributingSources()
:成员方法,返回一个包含贡献源信息的数组,表示当前接收器接收到的媒体流的贡献者。
getStats()
:成员方法,返回一个 Promise,该 Promise 在解析后会提供有关接收器的统计信息。
getCapabilities(kind)
:静态方法,用于获取特定类型(音频或视频)的编解码器能力信息。参数 kind
可以是 “audio” 或 “video”。
示例 以下是一个使用 RTCRtpReceiver.getCapabilities
方法来获取音频和视频编解码器能力信息的示例代码:
1 2 3 4 5 6 7 const videoCapabilities = RTCRtpReceiver .getCapabilities ('video' );console .log ("Video Codecs:" , videoCapabilities.codecs );const audioCapabilities = RTCRtpReceiver .getCapabilities ('audio' );console .log ("Audio Codecs:" , audioCapabilities.codecs );
这段代码中,我们使用 RTCRtpReceiver.getCapabilities
方法来获取音频和视频编解码器的能力信息。在真实使用当中,我们可以设置传输的音视频的编解码器 然后,我们打印出返回的 codecs
属性,其中包含了关于支持的编解码器的详细信息,例如编解码器的类型、编码器和解码器参数等。
其他名词术语解释 当涉及到 WebRTC 和实时通信时,以下术语是很常见的:
SDP(Session Description Protocol) :会话描述协议,用于描述多媒体会话的参数,例如编解码器信息、媒体流传输方式等。在 WebRTC 中,SDP 用于建立和管理对等连接。
RTCP(Real-Time Control Protocol) :实时控制协议,是用于在 RTP(实时传输协议)会话中提供控制和监控功能的协议。RTCP 主要用于传输统计信息和控制信息。
RTP(Real-Time Transport Protocol) :实时传输协议,是一种用于在 IP 网络上传输多媒体数据的协议。RTP 通常与 RTCP 一起使用,用于在网络上传输音频和视频流。
ICE(Interactive Connectivity Establishment) :交互式连接建立,是一种用于在两个设备之间建立网络连接的技术。ICE 使用一系列技术(包括 STUN、TURN 和 ICE)来克服网络地址转换(NAT)和防火墙等网络障碍。
这些术语在 WebRTC 中起着至关重要的作用,它们共同构建了实时通信系统的基础。
WebRTC的工作原理 WebRTC(Web Real-Time Communication)
是一项用于实现浏览器之间实时通信的开放标准技术。它允许在不需要额外插件的情况下,通过浏览器直接进行音频、视频和数据传输。WebRTC
技术的出现,极大地促进了在线会议、远程教育、视频直播等领域的发展。下面将深入探讨 WebRTC
的核心技术,包括媒体获取 、媒体传输 、信令交换 以及NAT穿越 等方面,并结合详细的例子进行说明。
navigator.mediaDevices.getUserMedia()
是 WebRTC 提供的方法之一,用于从用户的摄像头和麦克风中获取媒体流。它允许 Web 应用程序访问用户的音视频设备,并将其转换为可用于实时通信、音视频录制等目的的媒体流。
示例代码 以下是关于 navigator.mediaDevices.getUserMedia()
方法的例子:
1 2 3 4 5 6 7 8 9 navigator.mediaDevices .getUserMedia ({ video : true , audio : true }) .then ((stream ) => { const videoElement = document .getElementById ('localVideo' ); videoElement.srcObject = stream; }) .catch ((error ) => { console .error ('获取用户媒体设备失败:' , error); });
参数说明
constraints
是一个可选参数,用于指定获取媒体流的约束条件。
可以包含两个属性:
video
:指定是否捕获视频。可以是布尔值或包含视频约束的对象。
audio
:指定是否捕获音频。可以是布尔值或包含音频约束的对象。
返回值
getUserMedia()
方法返回一个 Promise
对象。
如果用户授权并成功获取媒体流,则 Promise
对象的 then
回调函数会被调用,并传递一个 MediaStream
对象作为参数,其中包含捕获的视频和/或音频轨道。
如果用户拒绝权限或获取媒体流失败,则 Promise
对象的 catch
回调函数会被调用,并传递一个 Error
对象作为参数,其中包含错误的详细信息。
用途 获取到媒体流后,您可以将其用于各种用途,例如:
在 <video>
元素中播放捕获的视频流。
将视频流传输到远程对等端,实现实时视频通话或视频会议。
对音频流进行处理、录制或转发等操作。
权限和安全性
浏览器通常会在第一次调用 getUserMedia()
方法时向用户请求音视频设备的权限。
用户需要允许或拒绝权限请求,以控制是否允许网站访问其摄像头和麦克风。
为了保护用户隐私和安全,浏览器会限制某些情况下的媒体设备访问,例如当网页位于不安全的上下文中时。
总的来说,navigator.mediaDevices.getUserMedia()
方法为开发者提供了一种方便的方式来获取用户的音视频流,从而实现各种实时音视频通信、录制和处理等功能。
说明 上面的代码演示了如何使用 getUserMedia API
获取用户的音视频流,并将其绑定到一个 <video>
元素上进行实时预览。通过这种方式,我们可以轻松地在浏览器中获取用户的音视频数据,为后续的通信做准备。
navigator.mediaDevices.getDisplayMedia()
是 WebRTC 提供的方法之一,用于获取屏幕共享流。它允许用户捕获屏幕上的内容并将其转换为媒体流,以便在 Web 应用程序中进行处理和展示。
示例代码 让我们详细解释一下这个方法的用法和相关概念:
1 2 3 4 5 6 7 navigator.mediaDevices .getDisplayMedia (constraints) .then (stream => { }) .catch (error => { });
参数说明
constraints
是一个可选参数,用于指定获取屏幕共享流的约束条件。
可以包含两个属性:
video
:指定是否捕获视频。默认值为 true
。
audio
:指定是否捕获音频。默认值为 false
。
返回值
getDisplayMedia()
方法返回一个 Promise
对象。
如果用户授权并成功获取屏幕共享流,则 Promise
对象的 then
回调函数会被调用,并传递一个 MediaStream
对象作为参数,其中包含屏幕共享的视频和/或音频轨道。
如果用户拒绝权限或获取屏幕共享流失败,则 Promise
对象的 catch
回调函数会被调用,并传递一个 Error
对象作为参数,其中包含错误的详细信息。
用途 获取到媒体流后,您可以将其用于各种用途,例如:
在 <video>
元素中播放屏幕共享内容。
将屏幕共享流传输到远程对等端,实现屏幕共享功能。
对媒体流进行处理、录制或转发等操作。
权限和安全性
浏览器通常会在第一次调用 getDisplayMedia()
方法时向用户请求屏幕共享权限。
用户需要允许或拒绝权限请求,以控制是否允许网站捕获屏幕内容。
为了保护用户隐私和安全,浏览器会限制某些情况下的屏幕共享,例如当网页位于不安全的上下文中时。
说明 navigator.mediaDevices.getDisplayMedia()
方法为开发者提供了一种方便的方式来获取屏幕共享流,从而实现屏幕录制、远程教学、视频会议等各种实时协作和分享功能。
WebRTC
的媒体传输主要涉及使用 RTCPeerConnection
建立点对点的连接,并将音视频数据传输给其他 RTCPeerConnection
。这个过程涉及到 ICE
候选、会话描述等技术,以确保数据能够安全、高效地传输。
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 const peerConnection = new RTCPeerConnection ();peerConnection.onicecandidate = function (event ) { let candidateObject = {}; const candidate = event.candidate ; if (!candidate) { log.debug ('message: Gathered all candidates and sending END candidate' ); candidateObject = { sdpMLineIndex : -1 , sdpMid : 'end' , candidate : 'end' , }; } else { candidateObject = { sdpMLineIndex : candidate.sdpMLineIndex , sdpMid : candidate.sdpMid , candidate : candidate.candidate , }; if (!candidateObject.candidate .match (/a=/ )) { candidateObject.candidate = `a=${candidateObject.candidate} ` ; } } socket.sendSDP ( 'connectionMessage' , { roomId : 'xxx' , msg : { type : 'candidate' , candidate : candidateObject }, browser : 'xxx' } ); }; navigator.mediaDevices .getUserMedia ({ video : true , audio : true }) .then ((stream ) => { stream.getTracks ().forEach ((track ) => { peerConnection.addTrack (track, stream); }); }); peerConnection.createOffer () .then ((offer ) => { return peerConnection.setLocalDescription (offer); }) .then (() => { socket.sendSDP ( 'connectionMessage' , { roomId : 'xxx' , msg : { type : peerConnection.localDescription .type , sdp : peerConnection.localDescription .sdp , }, browser : 'xxx' } ); }) .catch ((error ) => { console .error ('创建Offer失败:' , error); });
说明 上面的代码演示了如何使用 RTCPeerConnection
建立点对点连接,并将本地的音视频流添加到连接中。然后,通过创建一个Offer
,并将其发送给对方,以发起一个通信会话。在这个过程中,我们还需要处理 ICE
候选,以便在连接过程中进行 NAT
穿越。
信令交换(Signaling) WebRTC
的信令交换主要涉及将会话描述、ICE
候选等元数据信息交换给其他 RTCPeerConnection
,以建立和管理通信会话。这个过程通常需要借助于信令服务器来协调客户端之间的消息传递。
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 const ws = new WebSocket ('ws://localhost:8080' );ws.onmessage = function (event ) { const message = JSON .parse (event.data ).msg ; if (message.type === 'offer' ) { peerConnection.setRemoteDescription (new RTCSessionDescription (message)) .then (() => { return peerConnection.createAnswer (); }) .then ((answer ) => { peerConnection.setLocalDescription (answer); return answer }) .then (() => { socket.sendSDP ( 'connectionMessage' , { roomId : 'xxx' , msg : answer, browser : 'xxx' } ); }) .catch ((error ) => { console .error ('处理Offer失败:' , error); }); } else if (message.type === 'answer' ) { peerConnection.setRemoteDescription (new RTCSessionDescription (message)) .then (() => { console .log ('成功建立通信连接!' ); }) .catch ((error ) => { console .error ('处理Answer失败:' , error); }); } else if (message.type === 'candidate' ) { const candidateParams = typeof (message.candidate ) === 'object' ? message.candidate : JSON .parse (message.candidate ); candidateParams.candidate = candidateParams.candidate .replace (/a=/g , '' ); candidateParams.sdpMLineIndex = parseInt (candidateParams.sdpMLineIndex , 10 ); const candidate = new RTCIceCandidate (candidateParams); peerConnection.addIceCandidate (candidate) .then (() => { console .log ('成功添加ICE候选!' ); }) .catch ((error ) => { console .error ('添加ICE候选失败:' , error); }); } };
说明 上面的代码演示了如何使用 WebSocket
进行信令交换。客户端通过 WebSocket
连接到信令服务器,并监听来自服务器的消息。当收到对方发送的 Offer
、Answer
或 ICE
候选时,客户端需要相应地处理并执行相应的操作,以建立和管理通信会话。
NAT穿越(NAT Traversal) NAT(Network Address Translation)
穿越是指在 NAT
环境下,通过一系列的技术手段实现两个位于私有网络中的计算机或设备之间的直接通信。WebRTC
通过使用 STUN
和 TURN
服务器来实现 NAT
穿越,以确保在各种网络环境下都能够正常进行实时通信。
示例代码 1 2 3 4 5 6 7 8 9 10 const configuration = { iceServers : [ { urls : 'stun:stun.l.google.com:19302' }, { urls : 'turn:your-turn-server.com' , username : 'username' , credential : 'password' } ] }; const peerConnection = new RTCPeerConnection (configuration);
添加传输通道 当通道和管理通信会话建立成功后,我们需要将我们本地的音视频流传输到远端的 RTCPeerConnection
中,所以我们需要调用 RTCPeerConnection.addTransceiver
方法。
addTransceiver()
方法用于向对等连接(RTCPeerConnection
对象)添加传输通道(transceiver),以便在单个轨道上发送和接收媒体流。通过这种方式,您可以更灵活地控制对等连接的行为,并更精细地管理媒体流的传输。
该方法接受一个或多个参数,具体取决于您要添加的传输通道的类型和配置。通常情况下,addTransceiver()
方法用于以下两种情况:
添加本地传输通道(用于发送) :您可以通过将本地媒体轨道(例如音频或视频轨道)添加到对等连接中来创建一个新的传输通道,以便将该媒体流发送给远端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const transceivers = [];localStream.getTracks ().forEach (async (track) => { const options = { streams : [localStream], }; if (track.kind === 'video' ) { options = { sendEncodings : generateEncoderParameters (), }; } const transceiver = peerConnection.addTransceiver (track, options); transceivers.push (transceiver); });
当需要移除的时候
1 2 3 4 5 6 7 8 9 transceivers.forEach ((transceiver ) => { console .debug ('Stopping transceiver' , transceiver); if (transceiver.mid === '0' ) { peerConnection.removeTrack (transceiver.sender ); } else { transceiver.stop (); } });
添加远程传输通道(用于接收) :当您接收到远端对等连接发送的媒体流时,WebRTC
会自动为每个远程媒体轨道创建传输通道,并将其添加到对等连接中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 peerConnection.ontrack = (event ) => { const remoteTrack = event.track ; if (trackEvt.streams .length !== 1 ) { console .warning ('More that one mediaStream associated with track!' ); } const stream = trackEvt.streams [0 ]; stream.onremovetrack = () => { if (stream.getTracks ().length === 0 ) { this .emit (ConnectionEvent ({ type : 'remove-stream' , stream })); } }; this .emit (ConnectionEvent ({ type : 'add-stream' , stream })); };
说明 上面的代码示例中,我们通过配置 ICE
服务器来实现 NAT
穿越。其中,STUN
服务器用于获取公网 IP
地址和端口,而 TURN
服务器则用于在无法直接通信的情况下进行中转。通过合理配置 ICE
服务器,我们可以在不同的网络环境中都能够顺利地进行实时通信。
设置 Codec 编解码器 获取可用 Codec 1 const supportsSetCodecPreferences = window .RTCRtpTransceiver && 'setCodecPreferences' in window .RTCRtpTransceiver .prototype ;
获取 Codec 通过 RTCRtpSender.getCapabilities('video')
获取可支持的 codec
。然后把它们放进列表 codecPreferences
里,通过 Select
元素展示,让用户选择想使用的 codec
。
1 2 3 4 5 6 7 8 9 10 if (supportsSetCodecPreferences) { const { codecs } = RTCRtpSender .getCapabilities ('video' ); const codecPreferences = []; codecs.forEach (codec => { if (['video/red' , 'video/ulpfec' , 'video/rtx' ].includes (codec.mimeType )) { return ; } codecPreferences.push ({ ...codec, value : (codec.mimeType + ' ' + (codec.sdpFmtpLine || '' )).trim () }); }); }
配置 Codec 找到用户选择的 Codec
。调用 transceiver.setCodecPreferences(codecs)
,把选中的 Codec
交给 transceiver
最上面,优先使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (supportsSetCodecPreferences) { let selectedIndex = 1 const preferredCodec = codecPreferences[selectedIndex]; if (preferredCodec.value !== '' ) { const [mimeType, sdpFmtpLine] = preferredCodec.value .split (' ' ); const { codecs } = RTCRtpSender .getCapabilities ('video' ); const selectedCodecIndex = codecs.findIndex (c => c.mimeType === mimeType && c.sdpFmtpLine === sdpFmtpLine); const selectedCodec = codecs[selectedCodecIndex]; codecs.splice (selectedCodecIndex, 1 ); codecs.unshift (selectedCodec); const transceiver = pc1.getTransceivers ().find (t => t.sender && t.sender .track === localStream.getVideoTracks ()[0 ]); transceiver.setCodecPreferences (codecs); } }
WebRTC的应用场景 WebRTC
技术已经被广泛应用于多个领域,包括:
视频会议WebRTC
使得在网页浏览器中进行高清视频会议成为可能,用户可以通过浏览器直接加入会议室,与其他参与者进行实时视频通话。
在线教育 教育机构可以利用 WebRTC
技术搭建在线教育平台,让老师和学生之间进行实时的远程教学,实现互动和教学资源共享。
社交应用 社交应用程序可以利用 WebRTC
技术实现实时语音和视频通话功能,让用户之间进行更加直观和自然的交流。
远程医疗 医疗机构可以利用 WebRTC
技术搭建远程医疗平台,实现医生和患者之间的远程会诊和医疗服务,提高医疗资源的利用效率。
WebRTC的未来发展趋势 随着实时通信需求的不断增加,WebRTC
技术也在不断发展和完善。未来,我们可以期待以下几个方面的发展趋势:
网络性能的提升 随着网络基础设施的不断改善和网络带宽的增加,WebRTC
技术将能够实现更高质量、更稳定的实时通信体验。
新的应用场景 随着 WebRTC
技术的成熟和普及,我们可以预见到它将被应用于更多领域,如工业控制、智能家居和虚拟现实等。
标准化和开放性WebRTC
作为一个开放标准的实时通信技术,将继续推动标准化工作的进行,以确保不同厂商和平台之间的互操作性和兼容性。
安全性和隐私保护 随着个人隐私和数据安全意识的提高,WebRTC
技术将会加强对通信内容的加密和隐私保护,以确保用户数据的安全性。
总结 WebRTC
技术的出现为实时通信应用提供了强大的支持,使得开发者可以轻松构建具有高质量和稳定性的实时通信应用。随着技术的不断发展和完善,我们有理由相信 WebRTC
将会在未来的数字化世界中扮演越来越重要的角色,为用户带来更丰富、更便捷的通信体验。