带你了解云游戏实现关键技术——WebRTC

云游戏作为一个极具潜力的新技术,正在游戏领域不断攻城略地。虽然云游戏的概念早在10~20年前就已经存在,但一直无法流行,其根本原因是技术上的制约。而现在,云游戏得以快速井喷发展,得益于网络技术,实时音视频技术的突破。本文将介绍云游戏的基础知识,并着重介绍云游戏的核心 WebRTC 技术的入门知识,最后通过伪代码向读者介绍通过 WebRTC 技术如何实现云游戏。通过本文的阅读,读者可以从更底层的视角全方面来了解云游戏和 WebRTC。

文章作者:nicolasxiao/chairlencai  腾讯前端高级工程师    
文章编辑:kanedongliu
原文:https://mp.weixin.qq.com/s/oeMNStJJ0IBR8DQu0PsZjg

1、云游戏技术简介 

1.1 云游戏基本介绍

近年来,云游戏的概念很火,先后诞生了不少云游戏平台,比如谷歌的Stadia、腾讯的先锋、Start等。

这项技术为玩家带来了全新的游戏体验,例如通过云游戏的方式体验3A大作,不再需要下载动辄5~60G的游戏安装包,也不需要昂贵的显卡,非常适合游戏的试玩场景。

云游戏也带来了一个很大的变革,就是无论3A大作游戏、手机游戏,还是游戏主机的独占游戏,都可以脱离对终端设备硬件的要求和依赖。

那么,云游戏的背后是如何实现的呢?

实际上,云游戏与我们常用的远程桌面原理很像,就是一种远程玩游戏的方式,更专业点叫交互式流媒体。

常规的流媒体形式,例如在线看电影、看电视剧,只是播放非实时的音视频画面,用户不能操作并得到任何反馈。

而交互式流媒体是视频流会根据用户的操作实时变化产生反馈,比如云游戏就会玩家对游戏的操作,将游戏的实时画面传输给用户。

可以通过以下图示来理解:

带你了解云游戏实现关键技术——WebRTC

在云游戏中,用户的设备充当终端,只负责显示云端的游戏画面并收集用户操作,可以是PC、电视、VR一体机或手机等。

游戏本身则是运行在云端服务器上。

玩家的终端设备通过网络将操作指令发送给云端服务器,而服务器将玩家指令回放到游戏上,获得游戏实时反馈画面,然后通过音视频流的方式发送给玩家的终端设备,由终端设备来播放游戏画面。

1.2 云游戏的优势

那么为什么要以这种形式玩游戏呢?如果我们玩游戏,直接下载不就可以了吗?

这里要说下云游戏的优势:

首先是硬件门槛低:玩家不再需要购买昂贵的游戏设备就能体验高品质的游戏。

其次是跨终端:玩家只需要有一台可以播放在线视频的智能设备。举个例子,你可以在PC上玩PS5上的云游戏。

最后是免下载:云游戏不需要下载到玩家的设备,就像我们点播在线视频一样,因此可以做到即点即玩。传统游戏玩家需要先下载安装才能进入游戏,需要一定的等待成本。免下载的优势让云游戏在分发领域有明显的优势。可以先让玩家试玩云游戏,再引导到完整版游戏,提升买量的转化率。

而对于游戏开发商来说,云游戏可以降低适配多端的研发成本:传统的开发模式下,将一款游戏移植到不同的平台需要付出不少的移植研发成本。使用云游戏技术,只需要将游戏云化,就可以提供给不同设备平台的玩家,免去了游戏移植的过程。

下图展示使用电视设备来运行云游戏:

带你了解云游戏实现关键技术——WebRTC

下图展示通过手机体验游戏残局的新玩法:

带你了解云游戏实现关键技术——WebRTC

1.3 云游戏核心技术

目前正是云游戏技术发展的高峰期,实现云游戏的技术目前逐渐明朗,所以优化的技术最终都将趋于同一套解决方案。

决定云游戏是否能够有优质的体验和具备性价比的成本,主要取决于以下几个核心技术:

1.3.1 网络通讯技术

网络的传输速率、延时和稳定性对云游戏音视频的实时性都起到关键的影响。随着5G网络的发展和各项网络性能的提升,这无疑有利于云游戏技术的发展和落地。

1.3.2 边缘计算技术

边缘计算对于解决云游戏设备的就近接入起到重要的作用。通过边缘计算,玩家可以连接最近的一台云游戏设备,从而获得更低的网络延时和更高的稳定性,对降低云游戏的卡顿率和提升游戏体验有显著的效果。

1.3.3 容器/虚拟化技术

早期的云游戏需要给一个玩家分配一台设备提供服务。如果有多个玩家同时在线,需要多台设备在线,设备成本非常昂贵。随着服务器虚拟化技术和容器技术的发展,硬件的机能得到充分压榨,每台物理设备都能通过虚拟化转换成多台逻辑设备。再加上容器化技术,可让逻辑设备更加轻量级,也让硬件的成本降到更低。现在一台物理设备可以通过虚拟化容器化技术支持更多的玩家同时在线,这项技术让硬件成本大幅下降,非常有利于云游戏的规模拓展。

1.3.4 高密度GPU

由于游戏高度依赖GPU进行画面和物理的运算,在云计算环境中,充分利用服务器GPU的方案是使用专业的GPU和GPU虚拟化技术,将一个物理GPU虚拟化成多个逻辑GPU,供云游戏使用。

1.3.5 流媒体技术

云游戏的实时音视频传输离不开编解码技术的发展,以及媒体传输技术的提升,这两项技术的结合能够让画质更高的同时延迟更低,带来更好的游戏体验。

1.4 为何云游采用 WebRTC

除开局域网内的串流方案,在广域网场景下,大部分云游戏几乎都采用了 WebRTC 作为游戏音画传输的技术方案。

这里的原因主要是云游戏对于画质,音质,操作反馈的实时性要求很高,而 WebRTC 这项技术的设计初衷就是为解决实时音视频通讯问题而诞生的。

WebRTC 提供了复杂网络建连机制,成熟的媒体传输技术,先进的拥塞控制算法,高效音视频编解码技术,都是为了保障音视频通讯的实时而打造的。

WebRTC 长久以来不断针对在音视频通讯场景,实时性,画质音质要求高等问题进行优化。而云游戏技术对于画质,音质,操作反馈的实时性要求很高,这一点 WebRTC 正好满足了云游戏的技术诉求。

目前包含腾讯先锋,网易云游戏,咪咕云游等都采用了 WebRTC 的技术方案。

下图是几个比较有代表性的云游戏平台:

带你了解云游戏实现关键技术——WebRTC

谷歌的 STADIA

带你了解云游戏实现关键技术——WebRTC

腾讯IEG先锋团队

 

带你了解云游戏实现关键技术——WebRTC

网易

 

带你了解云游戏实现关键技术——WebRTC

移动

2、WebRTC简介 

2.1 简介

WebRTC是一套音视频实时通讯的解决方案,通过整合了主流高效的音视频编解码技术,复杂网络穿透技术,网络拥塞算法等技术,WebRTC实现的实时音视频效果在业内得到普遍认可。

最大的特色是,它是浏览器原生支持的,这也意味着可以在网页上通过 WebRTC 实现实时音视频通讯的各种应用场景。当然,WebRTC 的类库是C++编写的,支持集成到客户端,服务端等。

带你了解云游戏实现关键技术——WebRTC

WebRTC 的前身是 GIPS,它是一家专门从事音视频实时互动引擎的公司,在音视频编解码,网络传输有很丰富的技术经验。谷歌为了实现给自身的浏览器 Chrome 整合实时音视频的能力,拓展 Web 的应用生态,收购了GIPS,并开源了 GIPS 的代码,把它改名为 WebRTC,作为浏览器 BOM 的接口集成到 Chrome 中。至此,Web 开发正式可以涉足实时音视频的领域。

虽然 WebRTC 的初衷是用在浏览器,但也可以运用在其他平台上。WebRTC 的 SDK 是开放独立的,所以 android, ios,PC 端,服务端都可以通过 SDK 使用到 WebRTC 的功能。

2.2 音视频实时通讯面临的问题

为什么在实时音视频这个领域,WebRTC 占据这么主导的地位呢?

现在实时音视频所面临的问题如下:

带你了解云游戏实现关键技术——WebRTC

首先需要了解一下音视频实时通讯的解决方案的复杂性。

一个音视频实时通讯的解决方案包含音视频采集模块,音视频编解码模块,网络传输模块,音视频渲染模块,网络建连模块。

每一个模块都有其难点。比如音视频编解码,需要解决不同客户端编解码器的兼容问题,需要有一套协商机制,能够判断双方是否能通讯,选用什么编解码器等。

又比如网络建连,由于现在IPv4资源不足,现在的设备在网络中的建立连接都需要比较复杂。

针对网络的稳定性,需要有一套优秀的拥塞控制算法来保证实时性和通讯质量。

相信有涉略过其中某个方向的读者应该知道,这里每一个方向都需要投入大量的研发力量去攻关,一步一步优化

2.3 优势

在这种背景之下,如果业内有一个各方面都表现非常优秀,背后有一个沉淀已久的专业团队支持,同时背靠谷歌这种技术超前的公司,并且还是完全开源的技术方案。

这些都是普通音视频实时通讯方案无法提供的。

相信如果没有很特殊的理由,技术的选型决策都会倾向于使用这样现成的方案,而不是自己造一个轮子。

虽然在WebRTC方案公布之前,有不少公司是通过自研的方式研发音视频实时通讯的方案。但鉴于 WebRTC的明显优势,自研方案的性价比就很低。一般是会在 WebRTC 的基础上进行调优,以符合具体的业务场景。

下面表格罗列了自研方案与 WebRTC 的比较

指标

自研系统

WebRTC

性能

与研发团队能力相关

Google企业级支持及庞大的社区支持

浏览器

不支持

原生支持

跨平台

需自行实现,工程量大

原生支持

插件化

需自行实现,工程量大

原生支持

网路传输

需自行实现,复杂度高

原生支持

服务质量

需自行实现,复杂度高

原生支持

稳定性

与研发团队能力相关

非常稳定,经过无数项目验证

更新速度

一般较慢

每月迭代一个版本

可以看出, WebRTC 占有比较明显的优势。

除非项目具有一些历史包袱,不然很难去拒绝 WebRTC 这样优秀的解决方案。

总结下来, WebRTC 有如下优势:

● 先进的网络建连机制

● 先进的音视频编解码技术

● 先进的拥塞控制算法

● 主流浏览器全面支持

● 跨平台

● 强大的技术支持(背靠 Google)

● 完全开源

2.4 应用场景

随着网络技术的不断发展,音视频编解码技术的不断进化, 线上实时音视频互动的场景越来越多,比如在线教育,视频会议,直播带货,面试系统等。这些场景都有音视频传输的实时性的诉求。比如,在线教育,老师讲课过程中,可以实时的向学生提问,实时观察学生的学习状态,来调整讲课的节奏。

带你了解云游戏实现关键技术——WebRTC

 3、WebRTC技术架构 

3.1 技术架构

WebRTC的架构分为 4 层,分别是:

3.1.1 接口层

接口层向开发者提供不同端的开发接口,比如在浏览器 BOM 中提供的 WebRTC 相关接口,或者面向客户端的 WebRTC C++ SDK,还有不少第三方基于 WebRTC 标准实现的 SDK。

3.1.2 session 层

session 层主要用于控制业务逻辑,一般是通过信令服务器来实现。通过媒体协商,收集 candidate,建立端到端的连接。

3.1.3 核心引擎层

核心引擎层包含了音频编解码模块,视频编解码模块,网络传输处理模块等。支持对音视频进行编解码,去除抖动,回音消除等,实现网络媒体流,数据流的安全稳定传输。

3.1.4 设备层

设备层是最底层的保证,包含媒体的采集设备,网络底层的通讯等。

WebRTC 分层如下图

带你了解云游戏实现关键技术——WebRTC

3.2 整体流程

WebRTC整体的流程可以看下图

带你了解云游戏实现关键技术——WebRTC

首先是音视频设备的检测,来源可以是摄像头,视频流,甚至是画布,或者桌面捕获画面,

然后 WebRTC 开始协商媒体,确认通讯的双方所支持的编解码能力,确定最适合的通讯方案,与此同时,也对双方的网络进行探测,选择最合适的连通方案,最后安装选定的方案进行建连。

在媒体协商和网络建连过程中, WebRTC 通讯双方需要借助信令服务器来交换各自收集到的信息。

整体流程可以参看下面的时序图

带你了解云游戏实现关键技术——WebRTC

下面章节也会根据这个流程依次讲解。

4、WebRTC 音视频采集 

WebRTC支持多种音视频编解码器,视频编解码支持 VP8, VP9, AV1,h264,音频支持 OPUS,PCM等。随着音视频编解码新技术的发展,WebRTC还会陆续吸收更多优秀的编解码器。

4.1 媒体数据结构

媒体分为两种,流(stream)和轨道(track)。

4.1.1 轨道

轨道(track)是用于封装一个音频数据或者视频数据的数据结构。

比如有音频轨道,视频轨道。

4.1.2 流

流(stream)是用来封装多个轨道的,如果一个音频轨道和一个视频轨道被封装在同一个流里,那么音视频就会对齐时间信息,实现音视频同步。

带你了解云游戏实现关键技术——WebRTC

在浏览器里可以通过 MediaStream 创建一个流:

const stream = new MediaStream();

通过 getAudioTracks 和 getVideoTracks 分别可以获得流里的音频轨道和视频轨道

const audioTracks = stream.getAudioTracks();
const videoTracks = stream.getVideoTracks();

4.2 音视频采集

音频采集可以通过设备的麦克风输入,视频采集可以有多种,比如通过设备的摄像头获取人物的实时录像,也可以通过浏览器的画布(canvas)获得实时的视频流,还能通过其他的流媒体服务提供媒体输入。

4.2.1 获取本机的媒体设备信息

通常在用户的设备上,比如用户的笔记本,或者专业的会议设备,不止一个摄像头或者麦克风设备,WebRTC 提供了接口可以获取用户本机的所有媒体设备,方便展示给用户选择。

下面代码演示如何获取用户的媒体设备,并打印出来:

async function getUserMediaDevices() {
    const devices = await navigator.mediaDevices.enumerateDevices();
    console.table(devices);
}

4.2.2 获取摄像头音视频流

通过 navigator.mediaDevices.getUserMedia 可以打开设备的摄像头,实时录制。

下面的代码演示了,通过打开摄像头,录制音视频,并把实时音视频展示在浏览器上。

async function startVideoChat() {
    try {
        const stream = await navigator.mediaDevices.getUserMedia({
            audiotrue,
            video: { width640height480 }
        });
    
        const video = document.querySelector('#chat-local-vid');
        video.srcObject = stream;
        video.addEventListener('loadedmetadata', () => video.play());
    
    } catch (err) {
        console.error(err);
    }
}

4.2.3 获取屏幕分享视频流

WebRTC 还支持屏幕录制的功能。这个功能在开发视频会议或者是面试系统非常有用。

下面的代码展示如何通过 WebRTC 实现屏幕分享:

async function shareDesktop(displayMediaOptions{
  try {
    const desktopStream = await navigator.mediaDevices.getDisplayMedia();
    
    const desktopShareVideo = document.querySelector('#desktop-share-video');
    desktopShareVideo.srcObject = desktopStream;
    desktopShareVideo.addEventListener('loadedmetadata', () => desktopShareVideo.play());
  } catch(err) {
    console.error(err);
  }
}

4.3 视频的指标

视频流占据音视频通讯中主要的地位,它的传输数据量大,同时给用户最直观的感受,画面卡顿,模糊都会带来不好的体验。

而影响视频质量的指标有以下三个:分辨率,帧率和码率。

4.3.1 分辨率

分辨率是指视频每一帧图像所含的像素,比如一帧图像是 1920 x 1080, 则它所含的像素就是 2073600,每一个像素是 4 个字节,相当于如果没有做任何压缩编码的情况下,每帧需要传输 8 MB 的数据,如果按照这种数据量进行传输,带宽的要求是难以满足的,所以需要对视频原始数据进行编码,通过压缩算法对数据流进行压缩。

4.3.2 码率

码率代表的是媒体流每秒传输的数据量,正常情况下,码率和分辨率是正相关的,分辨率越大,码率越大。码率也跟编码器相关,相同分辨率下,压缩比越高的编码器,传输码率越低。

但也有一种情况,就是分辨率大,但码率很小,这种情况说明在编码过程中压缩过多导致原始图像信息丢失。

带你了解云游戏实现关键技术——WebRTC

4.3.3 帧率

帧率代表每秒播放的帧数。一般电影放映,动画制作控制的帧率都是24帧以上,这是因为人眼对于24帧以上的画面播放,是无法察觉出图片的不连贯性的,会有画面是连续播放的错觉。而当帧率低于15帧时,人眼可以分辨出画面的不连贯,就会产生卡顿的感觉。

所以实际应用场景会希望获得拥有高分辨率高帧率的视频流,又具备较低的码率的视频流,就需要有出色的编解码器支持,目前主流的编解码器有 VP8, VP9, H264,都有广泛的运用,而 H265 虽然目前主流浏览器还没有支持,但通过一些浏览器内核定制化,或者是客户端应用都可以自行开启 H265 编解码的支持,获得更高的压缩率。

5、WebRTC建连机制 

5.1 整体流程

WebRTC的端到端的建连过程比较复杂,需要确定双方的媒体传输的兼容性,处理网络的复杂度,进行NAT穿透或者通过TURN中继连接。

建连过程的主要流程如下:

媒体协商:首先双方获取本地的媒体支持信息,并交换双方媒体支持信息,确定使用哪种编解码方式

ICE(交互式建连)过程:双方通过STUN或者TURN服务器获取通讯的IP端口(候选者 candidate),并交换IP端口信息,并依次尝试建立连接,直到连接成功为止。

建连主要流程依次是:

  1. 连接信令服务器
  2. 创建 RTCPeerConnection 对象
  3. 媒体协商(SDP协议)
  4. ICE 交互式建连(请求 STUN/TURN 服务器,获取 candidate (候选 IP 端口))

如下图所示

带你了解云游戏实现关键技术——WebRTC

5.2 信令服务器

WebRTC 建连过程需要双方交换媒体信息,candidate 信息,协商最终的连接方式,这个过程是需要有一个服务器来转发双方的信息的。

但 WebRTC 的方案里并没有提供标准实现,需要开发者自行实现这样的服务器。这个服务器就是 WebRTC 里常被提到的信令服务器。

信令服务器通常与实际实现的业务场景相关,需要包含业务相关逻辑,比如如何创建房间,加入房间等逻辑,业务之间存在差异,不适合标准化实现。

信令服务器是可以使用不同技术实现,不同框架实现,只要满足网络信息通讯,实时性的要求就行。比如 HTTP协议,XMPP协议,websocket协议都可以作为信令服务器。golang,nodejs,java等都可以用作开发信令服务器的语言。

信令服务器需要实现一下的基本功能:

  1. 房间管理:负责创建房间,管理加入房间的用户
  2. 转发媒体协商信息:向对方用户转发本机的媒体协商信息
  3. 转发用户网络连接信息(IP端口):向对方用户转发本机的外网IP端口
    带你了解云游戏实现关键技术——WebRTC

如上图所示,

WebRTC客户端首先连接信令服务器,加入房间。

然后用户开始建立 WebRTC 连接,先收集本机的媒体信息,同时也通过 STUN 和 TURN 服务器收集本机的外网IP 端口信息,并通过信令服务器转发给对方。

带你了解云游戏实现关键技术——WebRTC

5.3 RTCPeerConnection

RTCPeerConnection 是 WebRTC 的核心模块,它封装了网络处理模块,音视频引擎模块,服务质量等。

RTCPeerConnection 顾名思义就是 WebRTC 的端连接对象,它的功能贯穿了 WebRTC 的整个生命周期,从建立连接时的媒体协商,收集 candidate (候选IP 端口),NAT 穿透,交互式连接,到端到端连接通讯的流媒体传输,数据传输。可以说,它是 WebRTC 最重要的模块。

5.3.1 创建RTCPeerConnection

WebRTC 客户端连接信令服务器后就可以开始创建 RTCPeerConnection。

因为 RTCPeerConnection 对象一旦创建就会在内部开始收集媒体信息,连接 STUN 服务器获取本机的候选IP端口信息(candidate),所以创建 RTCPeerConnection 需要传入 ICE STUN TURN 服务器的信息

创建过程用下面代码可以实现:

const configuration = {
    iceServers: [
        {
            urls'stun:<stun 服务器 url>'
        }
    ]
};
const pc = new RTCPeerConnection(configuration);

5.3.2 绑定媒体

创建完 RTCPeerConnection,除了开始媒体协商和交互式建连之外,还需要将本地的媒体流与连接进行关联,这样连接成功后,对方才能接受到媒体流。

RTCPeerConnection 是通过 addTrack 函数来实现添加媒体轨道的。

我们可以先通过摄像头获取媒体流,再遍历媒体轨道,把每个媒体轨道绑定到 RTCPeerConnection 上,下面代码展示了这个过程:

async function bindTracksToConnection(pc: RTCPeerConnection{
    const stream = await navigator.mediaDevices.getUserMedia({
        audiotrue,
        video: { width640height480 }
    });
    for (const track of stream.getTracks()) {
        pc.addTrack(track);
    }
}

5.4 媒体协商

 

媒体协商是通讯双方彼此支持的媒体能力,最终确定共同支持的媒体能力进行通讯,如果某一项能力一方不具备,则协商失败,无法建立连接。

媒体能力包含了音视频编解码的类型,媒体传输协议类型。

举个例子,如果 A 和 B 进行 WebRTC 通讯, A 要求用 VP9 编解码视频流,但 B 不支持 VP9 的编解码器,那 A 继续尝试用 H264, 这时候 B 支持 H264,那协商就成功了。假设 B 对 A 所列举的编解码器都不支持,那协商就失败了,后续也无法建立连接。

带你了解云游戏实现关键技术——WebRTC

5.4.1 SDP 协议

媒体协商所使用的协议是 SDP 协议。

SDP 全称是 Session Description Protocol,是一种文本格式的协议,用于描述媒体的元信息,不包含媒体流的数据。SDP 是一个比较老的协议,已经广泛应用在其他通讯协议中,比如会话启动协议SIP,RTP协议,实时流协议RSP等。

媒体协商过程通过 SDP 来交换双方的编解码类型,加密算法等信息。

SDP 包含了两部分的信息:

  1. 会话描述:包含协议版本,会话创建者ID,会话名称,会话持续时长。会话描述只有一份。
  2. 媒体描述:媒体描述可以有多份。每一份代表一个媒体轨道的信息,包含编解码器类型,媒体地址,端口等信息。在 WebRTC中,SDP 的媒体地址和端口并没被使用。

SDP 的结构如下图所示

带你了解云游戏实现关键技术——WebRTC

SDP 的示例

v=0
o=tommy
s=SDP example
t=2873397496 2873404696
a=recvonly
m=audio 49170 RTP/AVP 0
m=video 49170 RTP/AVP 98
a=rtpmap:98 H264/90000
a=fmtp:98 profile-level-id=42A01E;packetization-mode=1;

m=video 下的 a=rtpmap:98 H264/90000 表示当前使用的是 H264的编码,并且编码频率是 90kHz

5.4.2 媒体协商流程

WebRTC 媒体协商流程一般是这样,双方假设为 A 和 B,A 是呼叫方, B 是被呼叫方。则流程如下:

  1. A 创建 offer (createOffer),即携带 A 本机媒体信息的 SDP 信息,调用 RTCPeerConnection 的 setLocalDescription 关联起来,并经由信令服务器转发给 B
  2. B 接收 A 的 offer, 调用自己的 RTCPeerConnection 对象的 setRemoteDescription 关联起来
  3. B 创建 answer(createAnswer),即携带 B 本机媒体信息的 SDP 信息,调用 RTCPeerConnection 的 setLocalDescription 关联起来,并经由信令服务器转发给 A
  4. A 接收来自 B 的 answer, 调用自己的 RTCPeerConnection 对象的 setRemoteDescription 关联起来

如下图所示,这是 WebRTC 规定的媒体协商标准流程

带你了解云游戏实现关键技术——WebRTC

5.5 交互式建连 ICE

ICE 全称是 Interactive Connectivity Establishment,意思就是交互式的连接建立,主要用于多媒体会话的连接建立。

因为现在IPv4还广泛使用,但IPv4的资源已经匮乏,所以大部分接入互联网的设备都不具备公网IP,而是通过NAT与外网连接,中间隔了一层NAT,这也造成端对端的连接变得复杂。

如果网络普及IPv6,IP资源充足的情况下,就不需要NAT,可以直接通过 IP 建连,情况就变得简单很多。

但当下还是要解决端对端连接时面临的NAT穿透问题,WebRTC 的 ICE 交互式建连正是能突破 NAT 的解决方案,ICE 通过探测双方的各种外网连接信息,并对这些信息逐个进行尝试,直到建立连接为止。

早期 WebRTC 会收集所有的外网连接信息,然后再依次尝试连接,但这样会造成建连的耗时变长。后面 ICE Trickle 方案被加入 WebRTC 中,这种方案就是一边收集外网连接信息,一边尝试连接,一旦连接成功,就停止收集外网连接信息。

5.5.1 候选 IP 端口(candidate)

外网连接信息是指当前设备的可被用来连接的候选外网IP 端口,在WebRTC 中被叫做 candidate,即候选连接信息,包含外网IP,端口,协议。

候选连接信息的种类有4种,按照优先使用的顺序依次是:

  1. host:本机候选者
  2. srflx:映射候选者
  3. prlfx:来自对称NAT的候选者
  4. relay:中继候选者

带你了解云游戏实现关键技术——WebRTC

5.5.2 ICE 过程

在了解 ICE 的过程,首先要知道一个概念,STUN 服务器。

STUN 服务器是用于给在NAT网络的设备提供其外网IP端口探测服务的服务器。

了解完 STUN服务器后,我们可以进入正题了解 ICE 的流程:

ICE 首先会尝试用本机的IP作为候选者进行连接,当本机具有外网IP或者两台设备都在同一个局域网内,则本机IP能够建连成功。

如果本机候选者连接失败,ICE会尝试向 STUN 服务器发起请求,经过一定的探测步骤, STUN 服务器会返回设备的候选连接信息。设备再通过候选连接信息尝试连接。

带你了解云游戏实现关键技术——WebRTC

向 STUN 服务器请求外网信息的过程,实际上就是用来探测本机的NAT外网IP端口及NAT 的类型,也是 NAT 穿透过程。

5.6 NAT 穿透

尽管WebRTC通信时会按照内网、P2P、relay这样的次序尝试链接,但是大部分情况下,通信双方都不在同一局域网内,对于WebRTC而言,P2P和relay才是它的主要应用场景。

P2P通信核心要解决的是两台主机通信的NAT穿越问题。

带你了解云游戏实现关键技术——WebRTC

NAT全称Network Address Translation(网络地址转换)。它在真实网络环境中随处可见,设计初衷是为了解决IPV4不够用问题——通过多台主机隐藏在同一个公网IP下,从而减缓了IPV4地址的消耗。使用NAT后,主机隐藏在内网中,外部很难访问内网主机,虽然增加了安全性,但是也增加了任意两台主机(不在同一内网下)的通信难度

如果两台不在同一内网的主机需要通信,则需要经过NAT的地址映射之后才能进行:

P2P通信需要解决三类:完全锥型NAT、IP限制锥形NAT、端口限制锥型、对称型NAT

5.6.1 完全锥型NAT

这种类型的NAT的特点是:一旦打洞成功,所有知道该洞的主机都可以通过它与内网主机进行通信。

带你了解云游戏实现关键技术——WebRTC

上图中的主机X,通过NAT访问外网主机B,就在NAT上打个洞(就是在NAT上建立一个内网映射表),一旦打洞成功,所有知道该洞的主机都可以通过它与内网主机进行通信。

以下为“洞”的信息,可以理解为一个简单的四元组

5.6.2 IP限制锥型NAT

这种类型的NAT相对于完全锥型NAT更严格。NAT打洞成功后,只有与之打洞成功的外网主机才能通过该洞与内网主机通信,而其他外网主机即使知道洞口也不能与之通信。

带你了解云游戏实现关键技术——WebRTC

上图中主机B打洞成功后,即使将洞的信息分享给A、C,A和C也不能访问主机X。但是B上的其它端口仍然能访问主机X

5.6.3 端口限制锥型

端口限制锥型NAT比IP限制锥型NAT更严格。除了像IP限制锥型一样检查IP地址之外,还会对端口进行检测。

带你了解云游戏实现关键技术——WebRTC

上图中主机B打洞成功后,将洞的信息分享给A、C,除了A、C无法访问主机X之外,B上的其它端口也无法访问主机X

5.6.4 对称型NAT

对称型NAT是4种NAT中对数据包检测最严格的。内网主机每次访问不同的外网主机时,都会生成一个新洞,而不像前3种NAT类型都是同一个洞

带你了解云游戏实现关键技术——WebRTC

上图B打洞成功后,此时只有主机B上相应的端口发送的数据才能穿越该洞。而主机A和主机X通信,则需要建立新洞

5.6.5 NAT 穿透

要进行 NAT 穿透,首先需要探测出设备的 NAT 类型,大部分 NAT 类型都可以进行穿透,除非两边设备都是对称型 NAT。如下图所示:

NAT 类型

对端NAT类型

能否穿透

完全锥形

完全锥形

完全锥形

IP 限制锥形

完全锥形

端口限制锥形

完全锥形

对称型

IP 限制锥形

IP 限制锥形

IP 限制锥形

端口限制锥形

IP 限制锥形

对称型

端口限制锥形

端口限制锥形

端口限制锥形

对称型

对称型

对称型

如果两边都是对称型 NAT,则不进行穿透,直接通过中继服务器建立连接。

下图是 NAT 类型探测的流程:

带你了解云游戏实现关键技术——WebRTC

从这张表中可以看到,完全锥型NAT以及IP限制型NAT可以与任何其他类型的NAT互通,而端口限制型NAT与对称型NAT、对称型NAT与对称型NAT之间互通的成本太高,所以遇到这两种情况时就不必再尝试NAT穿越了。

5.7 TURN 中继转发

当 NAT 穿透失败时,WebRTC 还提供了 TURN 中继转发的方式。

TURN 是一个服务器,用于转发通讯双方的信息。

通讯的双方向 TURN 服务器申请自己的转发地址(relay 地址),如果需要向通讯的对端发送消息,只需向对方的relay地址发消息,TURN 服务器就会转发给对方。

传统的 TURN 服务器可以不需要每端都申请 relay 地址,但 WebRTC 采用了更为简洁的通讯模型,即双方都通过 relay 地址来通讯。

WebRTC 下,TURN 的通讯流程如下图:

带你了解云游戏实现关键技术——WebRTC

  1. 主机 A 向 TURN 服务器申请 relayA 地址
  2. 主机 B 向 TURN 服务器申请 relayB 地址
  3. 双方交换 TURN candidate
  4. 主机 A 向 relayB 发送媒体消息
  5. 主机 B 向 relayA 发送媒体消息  

6、媒体传输 RTP 

WebRTC 建连成功后,就可以开始进行媒体传输了。

WebRTC 的媒体传输采用的是 RTP 和 RTCP 协议。

RTP 属于应用层传输协议,与 HTTP 协议是同一个层。

TCP是可靠传输协议,它可以保证数据在传输过程中不丢失、不乱序。但TCP有个很大的缺点,即其可靠性是以牺牲实时性为代价的。对于实时音视频通讯来说,是不能容忍的。

UDP属于不可靠传输协议,它不保证数据能可靠到达,也不保证数据有序,但它最大的优点就是传输速度快。

RTP 协议是基于 UDP 之上,解决了 UDP 的可靠性和抖动问题。

RTCP是RTP的控制协议,实现了对 RTP 的丢包控制等控制。如下图所示:

带你了解云游戏实现关键技术——WebRTC

每个 RTP 数据包都会带有序号,载荷类型,媒体轨道信息等。

带你了解云游戏实现关键技术——WebRTC

序号可用于检测丢包,如下图所示:

带你了解云游戏实现关键技术——WebRTC

因为不同轨道的媒体都在同一个 RTP 连接中传输,所以需要 SSRC 字段来区分不同的媒体轨道。同时不同的媒体轨道的 RTP 序号是独立的,如下图所示。

带你了解云游戏实现关键技术——WebRTC

7、数据通道 SCTP 

WebRTC数据通道是一种直接建立在浏览器之间的传输通道,专门用于传输非媒体流的数据。它的通讯不需要经过服务器中转,灵活性很高。

数据交换不经过服务器,不受服务器性能及带宽瓶颈的限制,同时减少了数据被拦截的概率。底层传输使用了DTLS,具有较高的安全性。上层使用SCTP,默认使用可靠且有序的方式进行数据传输。数据通道支持传输字符串、文件、图片等数据。

WebRTC在RTCPeerConnection的扩展接口提供了数据通道API的入口,这些API接收/发送数据的使用方法与WebSocket一致。

createDataChannel()方法会创建一个新的数据通道,用于传输图片、文件、文本、数据包等任意数据。

代码如下:

const pc = new RTCPeerConnection(options);
const channel = pc.createDataChannel("chat");
channel.onopen = (event) => {
  channel.send('Hi you!');
}
channel.onmessage = (event) => {
  console.log(event.data);
}

当发起端调用createDataChannel()方法创建数据通道时,应答端触发datachannel事件

可通过下面代码与对端通讯:

const pc = new RTCPeerConnection(options);
pc.ondatachannel = (event) => {
    const channel = event.channel;
  channel.onopen = (event) => {
      channel.send('Hi back!');
    }
    channel.onmessage = (event) => {
      console.log(event.data);
    }
}

 8、拥塞控制策略 

WebRTC是通过增加带宽、减少数据量、提高音视频质量、适当增加时延以及更准确的带宽评估等方法来提升音视频服务质量的。在这些方法中,减少数据量、适当增加时延和更准确的带宽评估被统称为拥塞控制。

WebRTC 的拥塞控制流程大概是这样的:

  1. 发送端启动时先设置一个初始带宽,然后发送音视频数据给接收端
  2. 接收端定期向发送端反馈数据的延时及丢包情况(通过RTCP)
  3. 发送端接收延时及丢包情况,输入到拥塞评估模块评估出新的带宽
  4. 拥塞评估模块通知编码器,让编码器重新调整码率
  5. 拥塞评估模块同时也通知Pacer模块,控制发送速度
  6. 不断重复上述步骤,从而实时的适应网络的情况

如下图所示:

带你了解云游戏实现关键技术——WebRTC

WebRTC 的拥塞控制系统由如下模块组成:

  1. 拥塞评估模块,包含 GoogREMB 算法, TransportCC 算法,丢包拥塞控制算法等
  2. 流控模块,包含音视频编码器,pacer,网络收发模块,RTCP 解析模块

WebRTC 的拥塞控制分为两种,一种是基于延时的拥塞评估,另一种是基于丢包的拥塞评估。

8.1 基于延时的拥塞评估

基于延时的拥塞评估算法有两种,GoogREMB 和 TransportCC。

GoogREMB 已经被淘汰,但仍包含在 WebRTC 中,用于兼容旧的协议。

TransportCC 算法是在 GoogREMB 算法之上优化而来的,具有更好的拥塞评估能力。

两个算法的主要差别在于 GoogREMB 的拥塞评估是在接收端进行的,而 TransportCC 的拥塞评估是在发送端进行的,更为直接高效。

GoogREMB 和 TransportCC 都是通过预测下一时刻网络的质量,来调整码流大小。

其原理是通过发包的延时差作为输入,通过滤波器拟合,推测下一时刻的延时差,当下一时刻的延时差变大,说明网络情况变差,需要降低码率,当延时差不变或者变小,说明网络质量好,可以增大码率,提高媒体质量。

两个算法使用的滤波器也是不同的,GoogREMB 采用的是卡尔曼滤波器,TransportCC 采用的是最小二乘法滤波器。

带你了解云游戏实现关键技术——WebRTC

8.2 基于丢包的拥塞评估

WebRTC 除了基于延时进行拥塞评估,还会基于丢包进行拥塞评估,最终从两方评估出来的带宽,选择其最小值进行码流的调控。

基于丢包的拥塞评估采用如下的规则:

8.2.1 丢包率小于 2%

表示当前网络质量很好,可以增加 8% 的码率,提高通讯的音画质量

8.2.2 丢包率介于 2%~10% 之间

表示当前的码率与网络质量相匹配,无需对码率进行调节

8.2.3 丢包率大于 10%

说明当前网络的质量差,需要降低码率(公式:(1-0.5×丢包率)×当前码率)

8.3 拥塞算法的选择

对于拥塞算法的性能评估可以从以下几个维度来评判:

8.3.1 拥塞控制的准确性

这个指标是指拥塞评估的结论跟实际的物理带宽的情况的接近程度,如果接近程度越高,则说明准确性更高,可以更好的利用带宽。

带你了解云游戏实现关键技术——WebRTC

8.3.2 与TCP并存的公平性

这个指标是指当拥塞控制的连接与 TCP 连接并存情况下,带宽的分配是否平均。

带你了解云游戏实现关键技术——WebRTC

8.3.3 同类连接的公平性

这个指标是指当使用同一种拥塞控制算法的不同连接并存下,带宽的分配是否平均。

带你了解云游戏实现关键技术——WebRTC

8.3.4 丢包情况下的表现

这个指标是指当丢包率高的情况下,算法对拥塞评估的准确性

带你了解云游戏实现关键技术——WebRTC

结合这几个维度,TransportCC 都具备比较优异的分数,所以目前 WebRTC 使用 TransportCC 作为拥塞控制算法。

 9、WebRTC 在云游戏中的运用 

9.1 云游运用到的 WebRTC 特性

云游戏主要运用了 WebRTC 的三个特性,

9.1.1 WebRTC 的复杂网络建连机制

WebRTC 能够在 NAT 等复杂网络环境下,为链接双方协商到最优的网络连接,从而保证建立连接后的媒体通讯条件是最优的状态,保证音视频的实时性。

云游戏通过 WebRTC 在玩家的游玩端与云游戏的设备之间建立稳定可靠的网络连接。

9.1.2 WebRTC 的音视频编解码传输,拥塞控制

WebRTC 提供了音视频的录制,编码,解码,播放所有环节的功能。

云游戏中运用了 WebRTC 来录制云设备中游戏实时的画面的声音,再通过网络流媒体传输,实时传送到玩家的游玩端,通过 WebRTC 解码,在玩家的设备播放实时的音视频。

这个过程,为了保证音视频的实时性, WebRTC 内部提供了实时的媒体传输质量反馈,并通过反馈信息,对媒体传输进行调节,从而保证实时性,这就是 WebRTC 的拥塞控制。

9.1.3 WebRTC 的数据通讯

WebRTC 除了提供音视频实时通讯,同时还提供了数据通讯通道,在进行音视频通讯的同时也支持其他数据的可靠传输。

云游戏就是运用了 WebRTC 的数据通讯通道,将玩家的操作指令,发送到云端的设备中。

整体云游戏 WebRTC 运行机制可以参考下图所示:

带你了解云游戏实现关键技术——WebRTC

9.2 云游 WebRTC 整体流程

这里侧重讲解 WebRTC 在云游戏中的运用,简化了其他模块的细节。

下图展示了云游戏 WebRTC 从初始化,建立连接到游戏画面音频传输,及玩家操作传输的完整过程。

带你了解云游戏实现关键技术——WebRTC

9.2.1 申请设备

带你了解云游戏实现关键技术——WebRTC

首先玩家打开云游戏客户端,客户端会根据游戏的 ID 获取具体的云游戏配置信息,然后用这些信息向云游戏后台服务申请分配一个云游戏设备。云游戏后台服务会根据用户的网络运营商线路进行设备调度,将最适合的设备分配给客户端。这个时候可能会调度不到合适的设备,客户端会进入排队,等待合适的设备。

如果已经分配到设备,客户端开始与信令服务器建连,并从信令服务器监听 WebRTC 的配置下发,这份配置包含了 WebRTC 建连需要的初始化信息,例如用于探测 IP 端口的 ICE 服务器配置等。而另一方面,云游戏设备也进行初始化,并连接信令服务器。

下面通过伪代码演示下这个过程:

云游戏客户端:

// 申请游戏设备
// 获取云游戏配置信息
const gameInfo = getGameInfo();
// 向云游戏后台申请设备
const deviceInfo = allocCloudGameDevice(gameInfo);

// 如果未能调度到设备,则进入排队,等待空闲设备
if (!deviceInfo) {
    enterQueue();
    return;
}

const signalServer = connectToSignalServer();

signalServer.on('WebRTCConfig', (config) => {
    setWebRTCConfig(config);
});

云游戏服务器:

// 根据用户网络信息调度设备
const device = scheduleDevices(userNetworkInfo);

if (device) {
    // 生成设备信息(包含信令服务器等信息)
    const deviceInfo = buildDeviceInfo(device);
    sendResponse(deviceInfo);
}

云游戏设备:

initDevice();

const signalServer = connectToSignalServer();

9.2.2 媒体协商

到这个时候,云游戏 WebRTC 建连的双方,即云游戏客户端和云游戏设备端,已经具备了初始化的信息,可以进入 WebRTC 建连阶段。

带你了解云游戏实现关键技术——WebRTC

这个阶段云游戏客户端和云游戏设备端主要就是运用上一阶段从信令服务器拿到的 WebRTC 初始化配置来初始化 WebRTC 的连接对象,生成本地的媒体信息,并创建 offer 和 answer,通过信令服务器交换 offer 和 answer。

可以参考下面伪代码:

云游戏客户端:

// 使用信令服务器拿到的 WebRTC 初始化配置
const peerConnection = new RTCPeerConnection(webRTCConfig);

const offer = peerConnection.createOffer();

peerConnection.setLocalDescription(offer);

// 通过信令服务器转发 offer SDP
signalServer.send(offer);

// 接收云游戏设备的 SDP
signalServer.on('answer', (answer) => {
    peerConnection.setRemoteDescription(answer);
});

云游戏设备:

// 使用信令服务器拿到的 WebRTC 初始化配置
const peerConnection = new RTCPeerConnection(webRTCConfig);

// 接收云游客户端的 SDP
signalServer.on('offer', (offer) => {
    const answer = peerConnection.createOffer();
    peerConnection.setLocalDescription(answer);
    // 通过信令服务器转发 answer SDP
    signalServer.send(answer);
});

9.2.3 交互式建连

ICE 交互式建连阶段,主要是 WebRTC 底层处理的,代码层面上不需要做太多的逻辑。这个过程与媒体协商是同时进行的。云游戏客户端和云游戏设备会分别向 STUN/TURN 服务器获取自身的 IP 端口候选信息,并通过信令服务器交换候选信息,并尝试建立连接。

带你了解云游戏实现关键技术——WebRTC

云游戏客户端:

signalServer.on('candidate', (candidate) => {
    peerConnection.addIceCandidate(
        new RTCIceCandidate(candidate)
    );
});

// 创建数据通道,用于传输玩家的操作指令
// 也可以用于接收云游戏设备里游戏的状态通知
peerConnection.createDataChannel('control-data');

云游戏设备的逻辑也类似,这里不再展示。

9.2.4 游戏音视频传输和控制传输

到这里,云游戏客户端与云游戏设备的连接通道已经建立,可以开始传输游戏的音视频流和转发玩家操作指令。

此时云游戏设备会启动游戏,并开始游戏画面和音频的录制,并将音视频流添加到 WebRTC 的 peerConnection 对象中。这个操作会触发云游戏客户端侧的 ontrack 事件。

如下伪代码所示:

// 当云游戏设备侧开始传输游戏的音视频流时,云游戏客户端会收到 ontrack 信息
peerConnection.ontrack = (trackEvent) => {
    // 如果媒体轨道是视频,则初始化视频组件,并将轨道绑定给组件
    // 这样就能显示游戏画面了
    if (trackEvent.track.kind === 'video') {
        initVideoElement(trackEvent.streams);
    }
    // 如果媒体轨道是音频,则初始化音频组件,并将轨道绑定给组件
    // 这样就能播放游戏声音了
    if (trackEvent.track.kind === 'audio') {
        initAudioElement(trackEvent.streams);
    }
}

另一方面玩家操作游戏的动作,会被云游戏客户端捕获,并打包成指令发送给云游戏设备,到达云游戏设备后,再在游戏上回放玩家的操作。

如下伪代码所示:

client.addEventLister('touch-events', (touches) => {
    const command = buildTouchesCommand(touches);
    dataChannel.send(command);
});

云游戏设备端这边会响应:

dataChannel.on('command', (command) => {
    const touches = parseTouchesCommand(command);
    game.sendTouchesEvent(touches);
});

云游戏 WebRTC 的实现机制大致就是如此。

 10、总结 

本文主要讲述云游戏技术的基础知识,云游戏采用 WebRTC 的原因,及 WebRTC 通讯全流程的基础支持,最后通过一个云游戏的伪代码项目,讲解 WebRTC 在云游戏里的运用。

在实际项目的云游戏,WebRTC技术要复杂很多,需要了解的细节也更多。本文仅作为抛砖引玉,希望能提供给读者一个入门云游戏或者实时音视频的全览知识。时间仓促,可能有纰漏错误,欢迎指正。

 关于我们 

MoonWebTeam目前主要成员来自于腾讯,致力于分享有深度的前端技术。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论