WebRTC 处理流程图:
WebRTC 终端,负责音视频采集、编码、NAT穿越、音视频数据传输。
Signal服务器,负责信令处理,如加入房间、离开房间、媒体协商消息的传递。
STUN/TURN 服务器,负责获取WebRTC终端在公网的IP地址,以及NAT穿越失败后的数据中转。
主要基础概念:
帧率:摄像头一秒钟采集图像的次数;一般情况下,一秒可采集30张-100张图片。帧率越高,视频越平滑,占用带宽越大。
编码帧:视频数据流通过编码器(H264/H265\VP8/VP9)压缩后的帧。
I 帧:关键帧。压缩率低,可以单独解码成一幅完整的图像。
P帧:参考帧。压缩率较高,解码时依赖于前面已解码的数据。
B帧:前后参考帧。压缩率最高,解码时依赖于前后已解码的帧,B帧后面的P帧要优先进行解码。
非编码帧:音视频设备拍摄的未经过编码的原视频帧。编码格式一般为YUV、RBG格式。
MediaTrack:媒体轨,可理解类似火车道的两条铁轨,每条轨数据是独立的,不会与其他轨相交;
MediaStream:媒体流,音视频数据流,可存放0个或者多个音频轨或者视频轨。
MediaDevices:该接口提供了访问(连接到计算机上)媒体设备(如摄像头、麦克风)以及截取屏幕的方法。MediaDeviceInfo: 表示每个输入/输出的设备信息:deviceId(设备唯一标识),label(设备名称),kind(设备种类,音频设备还是视频设备)。除非用户已授权访问媒体的权限(HTTPS请求),否则label字段始终为空。
SDP: Session Description Protocal 会话描述协议,用文本描述各终端(PC\Android\IOS)所支持的音频编解码器、设定的参数、使用的传输协议及音视频媒体等。其包括会话描述、媒体描述。
会话描述(v-m):SDP版本号、用户名(不关心可用 - 代替)、会话唯一标识(NTP时间戳)、版本号(每次会话数据修改,其值会递增),网络类型、IP类型、ip 地址 。
媒体描述:媒体类型、媒体格式、传输协议、传输的IP和端口。
WebRTC 中的SDP:
Session Metadata 会话元数据
Network Description 网络描述
Stream Description 流描述
Security Description 安全描述
Qos Grouping Descriptions,服务质量描述
//=============会话描述====================
v=0
o=- 7017624586836067756 2 IN IP4 127.0.0.1
s=-
t=0 0
...
//================媒体描述=================
//================音频媒体=================
/*
* 音频使用端口1024收发数据
* UDP/TLS/RTP/SAVPF 表示使用 dtls/srtp 协议对数据加密传输
* 111、103 ... 表示本会话音频数据的 Payload Type
*/
m=audio 1024 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126
//==============网络描述==================
//指明接收或者发送音频使用的IP地址,由于WebRTC使用ICE传输,这个被忽略。
c=IN IP4 0.0.0.0
//用来设置rtcp地址和端口,WebRTC不使用
a=rtcp:9 IN IP4 0.0.0.0
...
//==============音频安全描述================
//ICE协商过程中的安全验证信息
a=ice-ufrag:khLS
a=ice-pwd:cxLzteJaJBou3DspNaPsJhlQ
a=fingerprint:sha-256 FA:14:42:3B:C7:97:1B:E8:AE:0C2:71:03:05:05:16:8F:B9:C7:98:E9:60:43:4B:5B:2C:28:EE:5C:8F3:17
...
//==============音频流媒体描述================
a=rtpmap:111 opus/48000/2
//minptime代表最小打包时长是10ms,useinbandfec=1代表使用opus编码内置fec特性
a=fmtp:111 minptime=10;useinbandfec=1
...
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
...
//=================视频媒体=================
m=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96 97 99 98
...
//=================网络描述=================
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
...
//=================视频安全描述=================
a=ice-ufrag:khLS
a=ice-pwd:cxLzteJaJBou3DspNaPsJhlQ
a=fingerprint:sha-256 FA:14:42:3B:C7:97:1B:E8:AE:0C2:71:03:05:05:16:8F:B9:C7:98:E9:60:43:4B:5B:2C:28:EE:5C:8F3:17
...
//================视频流描述===============
a=mid:video
...
a=rtpmap:100 VP8/90000
//================服务质量描述===============
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack //支持丢包重传,参考rfc4585
a=rtcp-fb:100 nack pli
a=rtcp-fb:100 goog-remb //支持使用rtcp包来控制发送方的码流
a=rtcp-fb:100 transport-cc
...
媒体协商:媒体协商的作用就是让双方找到共同支持的媒体能力,如双方都支持的编解码器,从而最终实现彼此之间的音视频通信。
媒体协商流程如下(主要通过RTCPeerConnection对象进行呼叫方、被会叫方SDP交换、解析实现),
**NAT 穿越:**NAT穿越 使用UPD协议,NAT类型:
1.完全锥型
2.IP限制锥型
3.端口限制锥型
4.对称型
对称型与对称型、对称型与端口限制型无法穿越。
NAT探测逻辑图:
WebRTC 代码关键点实现:
1:检测设备
//可用来判断浏览器是否支持API
navigator.mediaDevices
//enumerateDevices()方法:获取媒体输入输出设备列表
navigator.mediaDevices.enumerateDevices
上面实例:kind 设备种类包含:audioinput、audiooutput、videoinput等;groupId标识为一个设备即包含输入设备也包含输出设备,例如带话筒的耳机。本机访问未通过https请求授权,故label字段为空。
2:启动摄像头
通过mediaDevices.getUserMedia
方法打开摄像头(mediaStreamContrains里要配置video,否则不会启动摄像头)并获取视频流数据 给页面 video标签进行播放。
<!--前端代码-->
<div><video autoplay playsinline id="player"></video></div>
……
//通过设置contranis 对音视频做详细的参数配置
const mediaStreamContranis={
video : {//视频
width: 640, //视频框宽
height: 480,//视频框高
frameRate:15,//帧率
facingMode: 'enviroment'//前/后置摄像头
},
audio:{//音频
echoCancellation:true, //回音消除
noiseSuppression:true, //降噪
autoGainControl:true //自动增益
}
}
function start(){
……
//获取的音视频数据
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMediaStream)
.catch(handleError);
}
……
function gotMediaStream(stream){
videoplay.srcObject = stream;
}
3:拍摄照片并开启滤镜效果
界面canvas标签用来接收并展示抓拍到的照片。导入滤镜css样式为后续开启摄像头滤镜准备。
<div><canvas id="picture"></canvas></div>
<style>
.none {
-webkit-filter: none;
}
.blur {
-webkit-filter: blur(3px);
}
.grayscale {
-webkit-filter: grayscale(1);
}
.invert {
-webkit-filter: invert(1);
}
.sepia {
-webkit-filter: sepia(1);
}
</style>
为画布canvas开启滤镜(此处是展示端显示滤镜效果)。
picture.className = filtersSelect.value;
调用canvas的drawImage方法进行视频抓拍(如果需要将抓拍照片保存为有展示的滤镜效果的图片,需要在调用drawImange方法时设置filter为滤镜样式。)
var ctx = picture.getContext('2d');
ctx.drawImage(image,dx,dy,dWidth,dHeight);
ctx.filter = '选择的滤镜样式';
参数:
image:可以是一张图片,也可以是HTMLVideoElement(摄像头抓拍即此类型)。
dx,dy: 图片的起点坐标,x,y。
dWidth:图片的宽度。
dHeight:图片的高度。
4:录制本地视频并播放
创建录制对象new MediaRecorder(mediaStream,options)
,
参数:
mediaStream :要录制的媒体流;
options:设置MIME类型(如:video/webm, video/mp4 )和音频及视频的码率
//设置录制下来的多媒体格式
var options = {
mimeTypes:'video/webm;codecs=vp8'
}
mediaRecorder = new MediaRecorder(window.stream,options)
//当有音视频数据来了之后出发该事件
mediaRecorder.ondataavailable = handleDataAvailable;
//开始录制
mediaRecorder.start(10);//设置时间片段
……
/**
当该函数被触发后,将数据压入到blob中
**/
function handleDataAvailable(e){
if(e && e.data && e.data.size > 0){
buffer.push(e.data);
}
}
btnRecplay.onclick = function(){
var blob = new Blob(buffer,{type:'video/webm'});
recplayer.src = window.URL.createObjectURL(blob);
recplayer.controls = true;
recplayer.srcObject = null;
recplayer.play();
}
mediaRecorder.ondataavailable 绑定ondataavailable 事件,接收到音视频数据时调用handleDataAvailable方法(自定义方法)将录制的视频数据放入buffer中,作为后续播放的Blob数据源。
mediaRecorder.start(10) 设置时间片段,保证录制的视频分为多个一个固定长度(10ms)的片段存储,方便后续播放。
5:共享桌面
WebRTC共享桌面功能与共享音视频流类似,且WebRTC的远程桌面又不需要远程控制,其处理过程使用视频方式,而非传统意义上的RDP/VNC等远程桌面协议。
其共享流程:PC桌面抓取 (DirectX)–> 录制桌面(编码) --> 共享远端(传输,WebRTC传输过程中数据可丢失) -->远短观看 (解码) -->远短渲染画面(GPU、OpenGL/D3D)
抓取桌面Windows为例:
mediaDevices.getDisplayMedia()方法跟打开摄像头mediaDevices.getUserMedia()方法调用方式类似,
navigator.mediaDevices.getDisplayMedia(constraints)
.then(gotMediaStream)
.catch(handleError);
远程桌面控制,需要借助信令服务器将远端的操作(鼠标位置和点击事件、键盘操作)转换成PC操作指令数据发回被操作端,被操作端解析并执行指令。