webrtc学习记录三【创建基于RTCPeerConnection本机内的1v1音视频互通】
系列文章目录
webrtc学习记录一【媒体录制MediaRecorder】
webrtc学习记录二【基于socket.io创建信令服务器聊天室】
目录
系列文章目录
前言
一、媒体能力的协商过程
1.1、RTCpeerConnection类的使用
基本格式
参数说明
configuration详细说明
iceServers详细说明
常见配置示例:
1.2、RTCpeerConnection媒体协商的实例方法
createOffer
基本格式
示例
createAnswer
基本格式
示例
setLocalDescription
基本格式
示例
setRemoteDescription
基本格式
示例
addTrack
基本格式
参数说明
示例:
removeTrack
基本格式
addIceCandidate
基本格式
1.3、RTCpeerConnection媒体协商的实例的事件。
onnegotiationneeded
onicecandidate
ontrack
二、端对端连接的基本流程
三、实战,创建一套本机内的1:1音视频互通。
页面基础结构
采集本地数据流
连接远端
创建peerConnection
添加媒体流到peerConnection
本地端开始创建offer
处理offer信息
处理answer信息
处理onicecandidate接收事件
添加远端流
前言
记录webrtc学习过程中的要点,以便温故知新。本章主要是基于RTCPeerConnection,创建一套本机内的1v1音视频系统,没有通过UDP服务器和信令服务器的中转。
提示:以下是本篇文章正文内容,下面案例可供参考
一、媒体能力的协商过程
上图模拟了A和B进行互通时的媒体协商过程,
A需要创建一个offer信息,然后将offer信息通过setLocalDescription设置给peerConnection A;同时,发送offer到信令服务器。
信令服务器接收到A发送过来的offer之后,转发给B,B将来自A的offer信息通过setRemoteDescription设置到远端连接(因为对于B来说,A发送来的信息就是远端)
然后B创建一个answer,然后将answer信息通过setLocalDescription设置给peerConnection B;同时,发送answer信息到信令服务器。
信令服务器接收到B发送过来的answer之后,转发给A,A将来自B的answer信息通过setRemoteDescription设置到远端连接(因为对于A来说,B发送来的信息就是远端)
这样A和B的peerConnection就互相拿到了对方的sdp信息,从而能够实现互通。
1.1、RTCpeerConnection类的使用
上述流程的实现都是基于类RTCpeerConnection,因此下面就开始介绍一下它的用法和相关api
基本格式
var myPeerConnection = new RTCPeerConnection([configuration])
参数说明
参数 | 说明 |
configuration | 一个对象集合,包含许多配置项。一般主要配置ice服务器地址点对点连接。 |
configuration详细说明
configuration的详细参数包含如下:
参数 | 说明 |
iceServers | 最关键的一项,设置STUN 或 TURN服务的地址。不填则限制为本地 |
iceTransportPolicy |
指定ICE传输策略: relay:只是用中继候选者 all:可以使用任何类型的候选者,包含中继,host类型。但是国内网络基本只有中继能连通、 |
bundlePolicy |
'balanced',音轨与视轨分别使用音频和适配的通道,如2个音轨2个视轨,只要2个通道。 'max-compat',每个轨使用自己的传输通道,如3个视轨需要3个通道 'max-bundle',将音轨和视轨都绑定到同一个传输通道,只使用1个通道 |
rtcpMuxpolicy |
'negotiate',收集RTCP与复用RTP复用的ICE候选者,如果RTCP能复用就与RTP复用,如果不能复用,就将他们单独使用。 'require',只能收集RTCP与RTP复用的ICE候选者,如果RTCP不能复用,则失败。 默认是'require' |
peerIdentity | 建立对等连接时的标识字符串,可以不设置,默认是null |
certificates |
授权可以使用连接的一组证书,不设置会默认提供一个。 每一个可连通的候选者都需要一个证书,复用情况下有1个即可。 |
iceCandidatePoolSize |
16位的整数值,用于指定预取的ICE候选者的个数。 默认值为 0(意味着不会发生候选预取) 改变 ICE 候选池的大小可能会触发 ICE 收集的开始。 |
iceServers详细说明
数组类型,可以设置多个STUN或TURN服务地址,每一个都是一个ICE代理的服务。
属性 | 含义 |
credential | 凭据,只有TURN服务使用 |
credentialType | 凭据类型,可以是password或者oauth |
urls | 用于连接服务中的url数组 |
username | 用户名,只有TURN服务使用 |
常见配置示例:
const peerConnectionConfig = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302','credential': 'mypasswd','username': 'myusername'}]
}const myPeerConnection = new RTCPeerConnection(peerConnectionConfig )
1.2、RTCpeerConnection媒体协商的实例方法
以上面创建的myPeerConnection 实例为例,下面介绍一些必须的示例方法。
createOffer
基本格式
aPromise = myPeerConnection.createOffer([offerOptions])
offerOptions
属性 | 含义 |
offerToRecieveAudio |
0或者1,0表示不传输音频,1表示传输音频 |
offerToRecieveVideo |
0或者1,0表示不传输视频,1表示传输视频 |
voiceActivityDetection |
true/false,默认true,
是否开启静音检测,默认开启。不说话时过滤背景音。 |
iceRestart |
true/false, 该选项会重启ICE,重新进行Candidate收集。可以查看sdp中的ufrag信息判断,对于同一个prconnection,false的时候,每次createOfferufrag不变,true则每次都变化。 缺点是消耗带宽,优点是反馈及时,网络变化时也能确保联通。 |
示例
myPeerConnection.createOffer({offerToRecieveAudio: 0,offerToRecieveVideo: 1,}).then(desc => {}).catch(err => console.log('本地offer创建失败', err))
createAnswer
基本格式
aPromise = myPeerConnection.createAnswer([answerOptions])
示例
this.pcRemote.createAnswer().then(desc => {}).catch(err => console.log('远端answer创建失败', err))
setLocalDescription
方法中的sessionDescription取的就是offer和answer中返回的desc
基本格式
aPromise = myPeerConnection.setLocalDescription(sessionDescription)
示例
this.pcLocal.createOffer(offerOptions).then(desc => {this.pcLocal.setLocalDescription(desc)
}).catch(err => console.log('本地offer创建失败', err))
setRemoteDescription
基本格式
aPromise = myPeerConnection.setRemoteDescription(sessionDescription)
示例
this.pcRemote.createAnswer().then(desc => {this.pcRemote.setRemoteDescription(desc)
}).catch(err => console.log('远端answer创建失败', err))
addTrack
基本格式
rtpSender = myPeerConnection.addTrack(track,stream...)
参数说明
属性 | 说明 |
track | 添加到RTCPeerConnerction中的媒体轨 |
sdpMid | 指定track所在的stream |
示例:
this.localStream.getTracks().forEach(track => {this.pcLocal.addTrack(track, this.localStream)
})
这里的localStream来自navigator.mediaDevices.getUserMedia,第一章的学习记录中有用到。
removeTrack
对应addTrack,移除轨道。
基本格式
myPeerConnection.removeTrack(rtpSender)
addIceCandidate
基本格式
aPromise = myPeerConnection.addIceCandidate(candidate)
candidate属性
属性 | 说明 |
candidate | 候选者描述信息 |
sdpMid | 与候选者相关的媒体流的识别标签 |
sdpMLineIndex | 在SDP中m=的索引值 |
usernameFragment | 包括了远端的唯一标识 |
1.3、RTCpeerConnection媒体协商的实例的事件。
onnegotiationneeded
进行媒体协商的时候触发的事件。
onicecandidate
当收到ice候选者的时候触发。
ontrack
媒体轨道添加的时候触发。
二、端对端连接的基本流程
了解了上述api之后,我们再详细的分解一下端对端连接的基本流程,为后续开发做好准备。
- 首先是A和B分别连接信令服务器signal,为后续通讯做准备。
- 然后A创建peerConnectionA连接,同时添加媒体流到本地视频窗口,以便显示本地视频内容。
- A的peerConnectionA连接创建完成后,开始创建offer,然后将offer信息设置到A的peerConnectionA中的setLocalDescription,并且A会对stun/turn服务器发起请求,期望stun/turn服务器返回A解析后的的候选者信息candidateA,同时将offer SDP信息发送给信令服务器。
- 信令服务器收到A的offer SDP信息后,马上通过信令服务器传给B。这时B需要先创建B的peerConnectionB,然后将前面信令服务器传过来的offer SDP通过setRemoteDescription设置到远端描述中(因为对于B来说,A传过来的信息就是远端信息)。接下来B开始创建answer,然后将B创建的answer SDP信息通过setLocalDescription设置到peerConnectionB的本地描述信息中。然后将B的candidate信息发送给stun/turn服务器上。同时将answer SDP信息发送给信令服务器。
- 信令服务器将answer SDP信息返回给A,A收到后将answer SDP信息通过setRemoteDescription设置到A的远端描述中。
- 当A收到了来自ICE服务器中返回的peerConnectionA的候选者信息candidateA后,将candidateA发送给信令服务器。
- 信令服务器接收到candidateA后,发送给peerConnectionB,peerConnectionB通过addIceCandidate方法,将candidateA设置给peerConnectionB
- 当B收到了来自ICE服务器中返回的peerConnectionB的候选者信息candidateB后,将candidateB发送给信令服务器。
- 信令服务器接收到candidateB后,发送给peerConnectionA,peerConnectionA通过addIceCandidate方法,将candidateB设置给peerConnectionA
- 至此,peerConnectionA和peerConnectionB就分别获取到了对方的candidate候选者信息,peerConnection的底层就会通过排序,链接检测,找到一个A和B之间的最优通信线路,这样就建立好了一个A和B之间的P2P通道。
- 最后通过ontrack事件,监听接收到的A的媒体流,展示到远端。这样A和B之间的p2p音视频互通就基本实现了。
三、实战,创建一套本机内的1:1音视频互通。
由于是 本机内互通,所以信令服务器和stun服务器中转的步骤全部可以忽略。
new RTCPeerConnection([configuration])中的configuration为空时,默认采用本机host来解析,所以我们可以直接拿到candidate
因此上述流程图修改成这样,蓝色线表示修改后的,可能有点乱,大家将就着看一下吧。。。o(╯□╰)o
看下最终需要实现的效果图:
页面基础结构
先写好dom结构
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>peerconnection点对点音视频互通(不含信令服务器)</title><style>.btn_wrap{display: flex;}.btn{margin-right: 15px;}.video_wrap{display: flex;}.col{margin-right: 20px;display: flex;flex-direction: column;}.sdp{width: 200px;}</style></head><body><div id="app"><div class="content"><div class="btn_wrap"><button class="btn" @click="startCollect">开始采集数据,只看的到本地</button><button class="btn" @click="call">呼叫,能看到远端</button><button class="btn" @click="hangup">挂断</button></div><div class="video_wrap"><div class="left col"><span>本地视频流:</span><video :width="videoSize" :height="videoSize" autoplay ref="localVideo"></video><span>offer SDP</span><textarea class="sdp" readonly :value="offerSDP" cols="30" rows="10"></textarea></div><div class="right col"><span>远端视频流:</span><video :width="videoSize" :height="videoSize" autoplay ref="remoteVideo"></video><span>answer SDP</span><textarea class="sdp" readonly :value="answerSDP" cols="30" rows="10"></textarea></div></div></div></div></body><script src="./js/socket.io.js"></script><script src="./js/adapter-latest.js"></script><script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script><script>var app = new Vue({el: '#app',data: {answerSDP: '',offerSDP: '',pcLocal: null,pcRemote: null,localStream : null,constraints: {audio: false,video: true},videoSize:200,},methods: {},created() {console.log('io', io)},})</script>
<script src="//cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init()</script>
</html>
采集本地数据流
先实现简单功能点,点击第一个按钮采集本地数据,这样本地video窗口就能显示。对应学习记录一中的内容。
/*** 开始采集,收集本地媒体流信息,并展示。
*/
startCollect() {if(!window.navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {console.log('the getUserMedia is not supported!')return false} else {window.navigator.mediaDevices.getUserMedia(this.constraints).then(stream => {this.localStream = streamthis.$refs.localVideo.srcObject = streamconsole.log('this.$refs.localVideo.srcObject', this.$refs.localVideo.srcObject)}).catch(err => {console.log('音视频采集错误!', err)})}
},
连接远端
点击第二个按钮,调用call函数,连接远端。
因为这里我们只模拟本机内的1v1互通,所以不需要信令服务器来中转,也不需要stun/trun服务器解析。
创建peerConnection
先分别创建本地和远端的peerConnection
this.pcLocal = new RTCPeerConnection()
this.pcRemote = new RTCPeerConnection()
添加媒体流到peerConnection
由于媒体流可能有多个,所以这里是一个数组结构,遍历后将每一个媒体轨道都添加上对应的媒体流。
this.localStream.getTracks().forEach(track => {this.pcLocal.addTrack(track, this.localStream)
})
本地端开始创建offer
createOffer返回的是一个promise,所以我们直接用then操作来处理后续获得offer之后的处理。
const offerOptions = {offerToRecieveAudio: 0,offerToRecieveVideo: 1,
}this.pcLocal.createOffer(offerOptions).then(getOffer).catch(err => console.log('本地offer创建失败', err))
处理offer信息
在前面的步骤拿到offer信息后,马上开始设置描述信息,发送sdp,创建answer
对应流程图中的这一步
const getOffer = desc => {this.pcLocal.setLocalDescription(desc)this.offerSDP = desc.sdp// send desc to signal// pcRemote receive desc from signalthis.pcRemote.setRemoteDescription(desc)this.pcRemote.createAnswer().then(getanswer).catch(err => console.log('远端answer创建失败', err))
}
处理answer信息
const getanswer = desc => {this.pcRemote.setLocalDescription(desc)this.answerSDP = desc.sdp// send desc to signal// pcLocal receive desc from signalthis.pcLocal.setRemoteDescription(desc)console.log('getanswer')
}
处理onicecandidate接收事件
虽然没有stun/trun服务器,但是RTCPeerConnection会默认采集本机host信息来解析candidate,所以依然会有onicecandidate事件。
// 这几个事件是异步回调。
/***
*/
this.pcLocal.onicecandidate = e => {// pcLocal send candidate to signal// pcRemote revieve candidate from signalconsole.log('pcLocal.onicecandidate')this.pcRemote.addIceCandidate(e.candidate)
}
this.pcRemote.onicecandidate = e => {console.log('pcRemote.onicecandidate')this.pcLocal.addIceCandidate(e.candidate)
}
添加远端流
最后,通过ontrack事件监听媒体轨是否添加成功,添加成功后将媒体流信息输出到界面上即可。
this.pcRemote.ontrack = e => {console.log('pcRemote.onTrack')this.$refs.remoteVideo.srcObject = e.streams[0]
}
完整代码见git地址:
https://github.com/Silent-Jude/webRtcLearning/blob/main/public/peerconnection_local/index.html
webrtc学习记录三【创建基于RTCPeerConnection本机内的1v1音视频互通】相关推荐
- HTML学习记录三 :创建电子邮件链接
学习网站:http://www.w3school.com.cn/html/index.asp <span style="font-size:18px;"><htm ...
- MySQL学习记录 (三) ----- SQL数据定义语句(DDL)
相关文章: <MySQL学习记录 (一) ----- 有关数据库的基本概念和MySQL常用命令> <MySQL学习记录 (二) ----- SQL数据查询语句(DQL)> &l ...
- eNSP学习记录一——创建网络拓扑并配置
eNSP学习记录一--创建网络拓扑并配置 一.创建网络网络拓扑 二.配置PC和路由的IP并保存 三.设置静态路由 一.创建网络网络拓扑 我目前也是个小菜,所以感觉路由器使用AR1220会好一点,既可以 ...
- css学习记录三:文本属性
css学习记录三:CSS文本属性 一.文本属性的作用 二.文本颜色 三.文本对齐 四.装饰文本 五.文本缩进 六.行间距 一.文本属性的作用 CSSS Text(文本)属性可定义文本的外观,比如文本的 ...
- Kafka学习记录(三)——Broker
Kafka学习记录(三)--Broker 目录 Kafka学习记录(三)--Broker 对应课程 Zookeeper存储的Kafka信息 Broker总体工作流程 Broker的服役和退役 Kafk ...
- 轻松构建基于 Serverless 架构的弹性高可用音视频处理系统
作者 | 罗松(西流) 阿里巴巴技术专家 本文整理自架构师成长系列 2 月 12 日直播课程. 关注"阿里巴巴云原生"公众号,回复 "212",即可获取对应直播 ...
- 基于WebRTC实现1v1音视频聊天室
一. 前言 WebRTC(Web Real-Time Communication)旨在将实时通信功能引入到浏览器,用户无需安装其他任何软件或插件即可在浏览器间进行实时通信功能.本文介绍基于 WebRT ...
- 新增微信小程序和WebRTC连麦直播等多项能力,即构实时音视频SDK再升级!
经过2018年小半年的闭关练功,即构ZEGO团队铸造了不少黑科技.本文将为你带来即构ZEGO实时语音视频SDK近半年新增能力和功能优化的最新进展. 更懂应用场景的语音视频云 作为全球领先的实时语音视频 ...
- 如何基于OSS和MPS,快速搭建音视频文件上传服务?
背景 本文主要介绍如何基于OSS服务和MPS的上传SDK,快速搭建一个音视频文件上传服务. 优势 使用MPS的上传SDK上传音视频文件,具有以下优势: 增加文件列表管理功能. 增加STS Token ...
最新文章
- 抽象工厂————三层架构
- 《信息可视化:交互设计(原书第2版)》——第2章基本概念
- AJAX的异步请求小例子
- axios打包放到服务器上接口地址404_如何把网页文件放到云虚拟主机
- 微服务之:从零搭建ocelot网关和consul集群
- 从位图数据取得位图句柄
- markdownpad2下载安装教程
- 修改数据库表数据的办法
- Vaughn Vernon谈云原生和反应式现状
- echarts词云第一次出现不了数据要刷新才能出现_隐秘的角落弹幕分析,制作词云,看看观众们对该剧的评价如何...
- 倍增法求LCA(最近公共最先)
- [hdu5628]Clarke and math(dirichlet卷积)
- HP-Socket精简示例
- Netd 中 NetworkManager 分析
- 下载软件时的X86和X64的区别
- H5端input标签通过手机软键盘进行搜索,并关闭软键盘
- 【论文翻译笔记】Seamlessly Unifying Attributes and Items: Conversational Recommendation for Cold-Start User
- One Step By One Step 解析OkHttp3 - Dispatcher (一)
- 电商分销APP开发功能模块
- html关于页内样式,HTML的页内样式演示案例
热门文章
- wordpress自动发布_如何在WordPress中跟踪发布想法
- 文字向上滚动+动画(改进版)
- 命令行字体批处理工具——AFDKO(Adobe Font Development Kit for OpenType)
- 会计电算化什么是计算机硬件,2013年中央国家机关《会计电算化》知识点:计算机硬件...
- 51单片机入门篇-LED点亮以及流水灯
- 谷歌浏览器(google)设置翻译中文,翻译选项不生效或没有弹出翻译选项
- [转]ARM9 2410移植之Nand flash 驱动的编写与移植
- 币易Coinyee烎新年 每天送出200万ECO
- QLabel设置超链接
- 16-U盘重装纯净版Windows10系统以及激活(U盘+PE+UEFI+小米游戏本)