WebRTC媒体协商及实践

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操作指令数据发回被操作端,被操作端解析并执行指令。

 
 转自:https://blog.csdn.net/fuxuan_7/article/details/109002699
原文地址:https://www.cnblogs.com/javalinux/p/14446759.html