推荐使用WebRTC兼容库: adapter.js,用来抹平各个浏览器之间的差异
RTCPeerConnection相关
constructor构造函数
通过调用构造函数,返回一个RTCPeerConnection实例,表示本端与对端的一条连接。
const pc = new RTCPeerConnection(?configuration)
为了提高代码的健壮性,可以从多个属性检测构造函数:window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection
configuration(可选):新建连接的参数,可见MDN文档,常见的是添加iceServers,如
const configuration = {iceServers: [{urls: 'stuns:stun.example.org'}]}; const pc = new RTCPeerConnection(configuration); 复制代码
不过我们一般使用手动候选ice添加居多
Event事件
onaddstream:收到addstream
事件时调用的事件处理器,当MediaStream
被远端机器添加到这条连接时,该事件会被触发。
onicecandidate:收到 icecandidate
事件时调用的事件处理器。当一个 RTCICECandidate
对象被添加时(setLocalDescription),这个事件被触发。
ontrack:收到track
事件时调用,可以从event中获取视频流。
Method方法
createOffer:生成一个offer,它是一个带有特定的配置信息寻找远端匹配机器(peer)的请求。这个方法的前两个参数分别是方法调用成功以及失败的回调函数,可选的第三个参数是用户对视频流以及音频流的定制选项。用于发起方
createAnswer:在协调一条连接中的两端offer/answers时,根据从远端发来的offer生成一个answer。这个方法的前两个参数分别是方法调用成功以及失败时的回调函数,可选的第三个参数是生成的answer的可供选项。用于应答方
setLocalDescription:改变与连接相关的本地描述,分别将offer和answer作为参数传入。例如
const offer = await peer.createOffer(); await peer.setLocalDescription(offer); const answer = await peer.createAnswer(); await peer.setLocalDescription(answer); 复制代码
setRemoteDescription:改变与连接相关的对端描述,将接收到对端的SDP作为参数传入
addIceCandidate:手动添加候选ICE,当本机当前页面的 RTCPeerConnection
接收到一个从远端页面通过信号通道发来的新的 ICE 候选地址信息,本机可以通过调用addIceCandidate()
来添加一个 ICE 代理。
close:关闭ICE代理,结束任何正在进行的 ICE 处理和任何活动的流。
createDataChannel:创建一个可以发送任意数据的数据通道(data channel)。常用于后台传输内容, 例如: 图像, 文件传输, 聊天文字, 游戏数据更新包, 等等。
const channel = pc.createDataChannel(label, ?options)
- label:通道标识
- options:配置项
- ordered:信息到达顺序是否和发出顺序一致,默认为true;
- negotiated:默认情况(false)下,一方使用createDataChannel创建通道,另一方使用ondatachannel事件监听,双方进行协商;或者(true)双方调用createDataChannel,使用协定的id;
- id:创建通道的ID,用于双方协定通道,取值范围0-65534;
- maxRetransmits:尝试在不可靠模式下重传数据的次数,默认为空;
- maxPacketLifeTime:不可靠模式下传输消息的最大毫秒数,默认为空;
const dataChannel = RTCPeerConnection.createDataChannel(label, ?options); channel.onopen = function(event) { channel.send('Hi back!'); // 发送 } channel.onmessage = function(event) { // 接收 console.log(event.data); } } 复制代码
addTrack:将一个新的媒体音轨添加到一组音轨中,这些音轨将被传输给另一个对等点。
// 获取视频流 let stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); // 将视频流添加到track stream.getTracks().forEach(track => { peer.addTrack(track, stream); }); 复制代码
getStats:获取WebRTC的状态,发送/接收包的数量、丢包数……可以传入参数( MediaStreamTrack
)来控制要获取的数据类型
const peer = new PeerConnection(); setInterval(async () => { const res = await peer.getStats() res.forEach(report => console.log(report)) }, 1000) 复制代码
除了RTCPeerConnection实例上有getStats方法,sender和reveiver上也有getStats方法,他们获取的report只有发送/接收的部分,是整体与局部的关系
peer.getSenders()[0].getStats() 复制代码
media相关
Event事件
ondevicechange:每当媒体设备(例如相机,麦克风或扬声器)连接到系统或从系统中移除时,devicechange
事件就会被触发,这时可以使用enumerateDevices
来更新设备列表
Method方法(需要授权)
enumerateDevices:请求一个可用的媒体输入和输出设备的列表,例如麦克风,摄像机,耳机设备等。 返回的 Promise
完成时,会带有一个描述设备的数组。
navigator.mediaDevices.enumerateDevices().then(device => { console.log(device) }) 复制代码
如果两个设备的
groupId
相同,说明两个设备是同一台,比如带麦克风的耳机,能检测到输入设备和输出设备,他们两个的groupId
相同
getDisplayMedia:提示用户选择捕获的屏幕,返回值是一条视频轨道和可选的音频轨道(参数是媒体流约束,具体描述可见MDN)
// 已经在HTML中声明video标签 const localVideo = document.querySelector("video"); /** * @description: 向video标签中注入视频流 * @param {*} mediaStream * @return {*} */ function gotLocalMediaStream(mediaStream) { console.log(mediaStream); localVideo.srcObject = mediaStream; } function screenShare() { // 屏幕捕捉 navigator.mediaDevices.getDisplayMedia({ video: { cursor: "always", // 总是显示光标 width: 1920, // 宽度 height: 1080, // 高度 frameRate: 60 // 帧率 }, audio: true }) .then(gotLocalMediaStream) } 复制代码
这里如果是捕获全屏和应用窗口会无法获取声音,捕获浏览器内的标签是可以获取声音的,这里不清楚具体原因,待日后再研究一下
getDisplayMedia:在获取用户授权后,调度用户的摄像机和麦克风,返回视频轨道和音频轨道(均可以来自虚拟源),参数同样是媒体流约束
function photo() { navigator.mediaDevices .getUserMedia({ video: { // 视频 width: 640, height: 480, frameRate: 15, facingMode: 'enviroment', // 设置为后置摄像头 /*'user': 前置摄像头 'environment': 后置摄像头 'left': 视频源面向用户但在他们的左边,例如一个对准用户但在他们的左肩上方的摄像机 'right': 视频源面向用户但在用户的右边,例如,摄像机对准用户但在他们的右肩上*/ deviceId: undefined // 设备id }, audio: true // 音频 }) .then(gotLocalMediaStream) .catch((error) => console.log("navigator.getUserMedia error: ", error)); } 复制代码
MediaRecorder相关
constructor构造函数
创建一个新的MediaRecorder
对象,对指定的MediaStream
对象进行录制,支持的配置项包括设置容器的MIME 类型 (例如"video/webm"
或者 "video/mp4"
)和音频及视频的码率或者二者同用一个码率
Const mediaRecorder = new MediaRecorder(stream, ?options);
- Stream: 要录制的流
- Options:
mimeType
: 为新构建的MediaRecorder
指定录制容器的MIME类型. 在应用中通过调用MediaRecorder.isTypeSupported()
来检查浏览器是否支持此种mimeType
。audioBitsPerSecond
: 指定音频的比特率。videoBitsPerSecond
: 指定视频的比特率。bitsPerSecond
: 指定音频和视频的比特率。此属性可以用来指定上面两个属性。如果上面两个属性只有其中之一和此属性被指定,则此属性可以用于设定另外一个属性。
Event事件
ondataavailable:当要录制的流有数据时触发
onstart/onpause/onresume/onstop:当开始、暂停、继续、停止事件触发时执行
Method方法
isTypeSupported(static):返回一个Boolean 值,来表示设置的MIME type 是否被当前用户的设备支持。
pause:暂停录制
resume:继续暂停处继续录制
start:开始录制,调用时可以通过给timeslice
参数设置一个毫秒值,如果设置这个毫秒值,那么录制的媒体会按照你设置的值进行分割成一个个单独的区块, 而不是以默认的方式录制一个非常大的整块内容。
stop:停止录制,返回一个录制的Blob。
var buffer; //当该函数被触发后,将数据压入到blob中 function handleDataAvailable(e) { if (e && e.data && e.data.size > 0) { buffer.push(e.data); } } function startRecord() { buffer = []; //设置录制下来的多媒体格式 var options = { mimeType: 'video/webm;codecs=vp8' } //判断浏览器是否支持录制 if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.error(`${options.mimeType} is not supported!`); return; } try { //创建录制对象 mediaRecorder = new MediaRecorder(window.stream, options); } catch (e) { console.error('Failed to create MediaRecorder:', e); return; } //当有音视频数据来了之后触发该事件 mediaRecorder.ondataavailable = handleDataAvailable; //开始录制 mediaRecorder.start(10); }