Webrtc流程学习-SimpleWebrtc源码阅读

SimpleWebrtc 的demo由三部分组成:
1 STUN/TURN 服务器(coturn)
2 信令服务器 (signalmaster) 一个简单的基于socketio的信令服务器(websocket)
3 前端HTML及JavaScript代码

而本文主要是阅读它的前端代码学习webrtc建立流程。因为里面涉及到很多事件机制,所以读起来还挺麻烦的。

这里通过SimpleWebrtc API 前端代码来学习webrtc原生API使用及各服务器在其中起到的作用。

1 源码结构

源码涉及部分有:

index.html
src
	peer.js
	simplewebrtc.js
	socketioconnection.js
	webrtc.js

以下为简单介绍
index.html: simplewebrtc 前端界面
simplewebrtc.js: SimpleWebRTC主要功能实现类,包含信令服务器实现类SocketIoConnection,webrtc基础API逻辑封装类 WebRTC,本身对外提供部分功能接口
socketioconnection.js: socketio封装类
webrtc.js: webrtc操作封装类,对外提供功能操作接口,包含Peer接口类,及码流实现,如getUserMedia。
peer.js: 数据传输类用于建立双方传输通道,是webrtc原生实现的封装,内部实现包含webrtc原生API :如RTCPeerConnection。


2 流程走读

先从理论上webrtc建立流程:
image.png

下面是Simplewebrtc 建立流程:

在这里插入图片描述

3 源码阅读

3.1 流程API关键解读

API 流程图

在这里插入图片描述


API解读:
(1)作为房主首先是链接上信令服务器,然后调用webrtc getUserMedia API获取本地码流,然后等待对端发送offer 消息。
(2) 加入者也是先连接上信令服务器,调用webrtc getUserMedia API获取本地码流,初始化webrtc RTCPeerConnection API,调用RTCPeerConnection.createOffer 生成offer消息,并通过信令服务器发送offer消息到对端。同时将offer传入RTCPeerConnection.setLocalDescription设置本地描述消息。该API调用后会触发RTCPeerConnection内部ICE信息收集机制与STUN服务器通信, 当获取到ICE信息后触发RTCPeerConnection**.**onicecandidate事件得到candidate消息。后续也通过信令服务器发送给对端。
(3)房主收到offer消息后,初始化webrtc RTCPeerConnection API,调用RTCPeerConnection.setRemoteDescription 配置对端描述信息,该API调用后会触发RTCPeerConnection.onaddstream事件,这里就可以将stream添加到前端界面了。(虽然ICE还没有协商成功,并么有数据)。配置完对端描述信息后,调用RTCPeerConnection.createAnswer 创建answer信息,并通过信令服务器将answer发送给对端。同时将answer传入RTCPeerConnection.setLocalDescription设置本地描述消息。该API调用后会触发RTCPeerConnection内部ICE信息收集机制与STUN服务器通信, 当获取到ICE信息后触发RTCPeerConnection.onicecandidate事件得到candidate消息。后续也通过信令服务器发送给对端。
(4)参与者收到answer消息后,调用RTCPeerConnection.setRemoteDescription 配置对端描述信息,该API调用后会触发RTCPeerConnection.onaddstream事件,这里就可以将房主的stream添加到前端界面了。虽然ICE还没有协商成功,并么有数据)。
(5)双方互发ICE所需candidate消息,调用RTCPeerConnection.addIceCandidate() 进行处理。RTCPeerConnection内部会机制进行撮合协商打通数据通道。

3.2 SimpleWebrtc相应代码解读

以下代码以缩进标识代码层次,以下代码为摘录的各功能较为关键API的API调用。

3.2.1 准备阶段

(1)第一个进入房间(房主)

房主(房间为空第一个进入房间)创建房间调用流程 到等待链接过程,代码走读

new SimpleWebRTC();
		new SocketIoConnection();//初始化socketio并与信令服务器链接
		connection.on('connect') //链接成功后触发connect事件进入出来函数
		self.testReadiness(); //判断码流是否ready
		new WebRTC(); //初始化WebRTC
			localMedia.call(this, this.config); //调用localmedia并初始化
			util.inherits(WebRTC, localMedia);//将调用localmedia 对象方法传递给WebRTC对象
		SimpleWebRTC.startLocalVideo();
			this.webrtc.start(this.config.media, function (err, stream){})//即调用的为调用localmedia的start方法
				navigator.mediaDevices.getUserMedia(constraints).then(function (stream){})//原生API获取码流
				this.webrtc.emit('localStream', stream);//本地码流ready对外发送信号
			attachMediaStream(stream, self.getLocalVideoContainer(), self.config.localVideo);  //将本地前端界面
		this.webrtc.on('localStream', function () 
    		SimpleWebRTC.testReadiness();
				SimpleWebRTC.emit('readyToCall', self.connection.getSessionid());
SimpleWebRTC.on('readyToCall', function (){})//作为房主会触发该参数 内部JoinRoom不会执行
SimpleWebRTC.createRoom()  //与信令服务器通信创建房间 发送create 消息

初始化SimpleWebRTC 初始化SocketIoConnection类 初始化WebRTC类,SocketIoConnection类链接到信令服务器。WebRTC类获取本地码流并添加前端界面。SocketIoConnection向信令服务器发送 create消息。然后等待信令服务器消息。

(2)加入者(后续进入房间者)
new SimpleWebRTC();
		new SocketIoConnection();//初始化socketio并与信令服务器链接
		connection.on('connect') //链接成功后触发connect事件进入出来函数
		self.testReadiness(); //判断码流是否ready
		new WebRTC(); //初始化WebRTC
			localMedia.call(this, this.config); //调用localmedia并初始化
			util.inherits(WebRTC, localMedia);//将调用localmedia 对象方法传递给WebRTC对象
		SimpleWebRTC.startLocalVideo();
			this.webrtc.start(this.config.media, function (err, stream){})//即调用的为调用localmedia的start方法
				navigator.mediaDevices.getUserMedia(constraints).then(function (stream){})//原生API获取码流
				this.webrtc.emit('localStream', stream);//本地码流ready对外发送信号
			attachMediaStream(stream, self.getLocalVideoContainer(), self.config.localVideo);  //将本地前端界面
		this.webrtc.on('localStream', function () 
    		SimpleWebRTC.testReadiness();
				SimpleWebRTC.emit('readyToCall', self.connection.getSessionid());
SimpleWebRTC.on('readyToCall', function (){})//作为参与者加入 加入时会执行joinRoom函数
SimpleWebRTC.joinRoom(room); //参与者执行joinRoom操作

初始化SimpleWebRTC 初始化SocketIoConnection类 初始化WebRTC类,SocketIoConnection类链接到信令服务器。WebRTC类获取本地码流并添加前端界面。因为均是初始化SimpleWebRTC类所以前面均一致。但本地视频流准备好后会触发readyToCall 事件,作为参与者,则会执行SimpleWebRTC.joinRoom(room)。进入双方建立链接流程。

3.2 建立链接

(1)加入者发送offer及candidate消息
SimpleWebRTC.joinRoom(room); //参与者执行joinRoom操作
	SocketIoConnection.emit('join', name, function (err, roomDescription) {})//获取到对端Clientid
	WebRTC.createPeer({})//根据对端Clientid 及对端config参数创建Peer(web对端config即自己config)
		new PeerConnection(); //创建rtcpeerconnection封装对象
			new RTCPeerConnection(config, constraints);//创建webrtc原生RTCPeerConnection对象
	WebRTC.on('createdPeer', function (peer){})//数据通道peer赋值
	Peer.start();//创建并发送offer
		PeerConnection.offer(this.receiveMedia, function (err, sessionDescription))//创建offer
    	RTCPeerConnection.createOffer()//原生API创建offer
			RTCPeerConnection.setLocalDescription(offer,function () {})//根据offer配置本地LocalDescription的本地描述SDP
			RTCPeerConnection.onicecandidate = this._onIce.bind(this);//当setLocalDescription,会主动收集ICE消息,收集到后会触发icecandidate
	 	PeerConnection.on('offer', function (offer) {})//当offer生成完成后 会使用信令服务器发送给对端
		PeerConnection.on('ice', this.onIceCandidate.bind(this));//ice事件触发后将上面API中收集到的candidate消息通过信令发送给对端

加入者加入房间后可以从信令服务器拿到当前房间client信息。根据这个client信息和config信息创建Peer,这里就会创建出RTCPeerConnection这个类。随后调用 RTCPeerConnection.createOffer() 创建offer,RTCPeerConnection.setLocalDescription 设置本地描述信息,通过信令服务器发送offer。当调用setLocalDescription 后会触发RTCPeerConnection.onicecandidate事件,该事件会返回所需ICE candidate信息。通过信令服务器发送candidate信息。

(2)房主接收offer并处理后发送answer
    SocketIoConnection.on('message', function (message) {})//接受信令消息
	WebRTC.webrtc.createPeer({})//根据offer消息创建对端Peer
		new PeerConnection(); //创建rtcpeerconnection封装对象
			new RTCPeerConnection(config, constraints);//创建webrtc原生RTCPeerConnection对象
	WebRTC.on('createdPeer', function (peer){})//数据通道peer赋值
	Peer.handleMessage(message);//处理出offer消息
		PeerConnection.handleOffer(message.payload, function (err) {})//
    		RTCPeerConnection.setRemoteDescription(new RTCSessionDescription(offer))//设置对端PeerConnection信息
			RTCPeerConnection.onaddstream = this.emit.bind(this, 'addStream');//当调用setRemoteDescription() 成功后会触发该事件,且该事件不会等待ICE协商成功
        PeerConnection.answer(function (err, sessionDescription){})//发送answer
		PeerConnection._answer(mediaConstraints, callback);
			RTCPeerConnection.createAnswer()//原生API创建answer
			RTCPeerConnection.setLocalDescription()//根据answer配置本地LocalDescription的本地描述SDP
			RTCPeerConnection.onicecandidate = this._onIce.bind(this);//当setLocalDescription,会主动收集ICE消息,收集到后会触发icecandidate
		PeerConnection.on('answer', function (answer) {})//通过信令服务器发送answer	
		PeerConnection.on('ice', this.onIceCandidate.bind(this));//ice事件触发后将上面API中收集到的candidate消息通过信令发送给对端
		PeerConnection.on('addStream', this.handleRemoteStreamAdded.bind(this));//添加对端码流 该事件会上传到顶层

作为房主一直监听信令服务器的message 消息,当收到offer消息时,会根据offer消息创建对端Peer,这里面也就会创建RTCPeerConnection类。创建该类后会进一步对offer消息进行处理后,会调用RTCPeerConnection.setRemoteDescription 设置远端描述信息,当代用该API后会触发RTCPeerConnection.onaddstream事件,添加对端码流,编写相应代码显示到前端。(在ICE未协商成功的情况下)。当处理offer完毕后,会调用RTCPeerConnection.createAnswer() API生成answer信息,并通过信令服务器将answer信息发送给远端,同时也会调用RTCPeerConnection.setLocalDescription 将answer传入,用于设置本地描述信息。调用setLocalDescription 信息后会触发onicecandidate事件收集ICE所需信息并通过信令服务器发送candidate信息给对端。

(3)加入者处理answer消息
 	SocketIoConnection.on('message', function (message) {})//接受信令消息
	Peer.handleMessage(message);//处理出answer消息
		PeerConnection.handleAnswer(message.payload)//
    		RTCPeerConnection.setRemoteDescription(new RTCSessionDescription(answer))//设置对端PeerConnection信息
				RTCPeerConnection.onaddstream = this.emit.bind(this, 'addStream');//当调用setRemoteDescription() 成功后会触发该事件,且该事件不会等待ICE协商成功
		PeerConnection.on('addStream', this.handleRemoteStreamAdded.bind(this));//添加对端码流 该事件会上传到顶层

加入者发送offer后,会等待对端消息,收到对端发来answer消息,会进行相应处理,RTCPeerConnection.setRemoteDescription设置远端描述信息,调用后会触发onaddstream事件,进而添加对端码流到前端界面(ICE协商未成功的情况下)

(4)双方收到candidate消息处理
 	SocketIoConnection.on('message', function (message) {})//接受信令消息
	Peer.handleMessage(message);//处理出answer消息
		PeerConnection.processIce(message.payload);//candidate 消息处理
			RTCPeerConnection.addIceCandidate();//将candidate的内容加入RTCPeerConnection中

在双方onicecandidate事件触发后,会给对端发送candidate消息,收到消息后会调用RTCPeerConnection.addIceCandidate()进行处理。而ICE内部协商打通数据通道部分应该在RTCPeerConnectionAPI内部实现。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网页视频开发 webrtc ;(function () { var logger = { log: function (){}, warn: function (){}, error: function (){} }; // normalize environment var RTCPeerConnection = null, getUserMedia = null, attachMediaStream = null, reattachMediaStream = null, browser = null, webRTCSupport = true; if (navigator.mozGetUserMedia) { logger.log("This appears to be Firefox"); browser = "firefox"; // The RTCPeerConnection object. RTCPeerConnection = mozRTCPeerConnection; // The RTCSessionDescription object. RTCSessionDescription = mozRTCSessionDescription; // The RTCIceCandidate object. RTCIceCandidate = mozRTCIceCandidate; // Get UserMedia (only difference is the prefix). // Code from Adam Barth. getUserMedia = navigator.mozGetUserMedia.bind(navigator); // Attach a media stream to an element. attachMediaStream = function(element, stream) { element.mozSrcObject = stream; element.play(); }; reattachMediaStream = function(to, from) { to.mozSrcObject = from.mozSrcObject; to.play(); }; // Fake get{Video,Audio}Tracks MediaStream.prototype.getVideoTracks = function() { return []; }; MediaStream.prototype.getAudioTracks = function() { return []; }; } else if (navigator.webkitGetUserMedia) { browser = "chrome"; // The RTCPeerConnection object. RTCPeerConnection = webkitRTCPeerConnection; // Get UserMedia (only difference is the prefix). // Code from Adam Barth. getUserMedia = navigator.webkitGetUserMedia.bind(navigator); // Attach a media stream to an element. attachMediaStream = function(element, stream) { element.autoplay = true; element.src = webkitURL.createObjectURL(stream); }; reattachMediaStream = function(to, from) { to.src = from.src; }; // The representation of tracks in a stream is changed in M26. // Unify them for e
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值