VR 眼镜的出现与快速发展让“赛博朋克”、“未来世界”不再遥远,通过手柄与音视频画面的互动,人们可以在娱乐、健身时体会到一种全面超越现有音视频的“沉浸式”体验。而在体验云游戏、大型全景赛事互动等应用时,如果想保持这种“身临其境”的“沉浸式”体验,还需要有超高清、高帧率的全景视频源、强劲的传输带宽和超低头动延时(MTP)。
视频源方面,因 VR 眼镜独有的 FOV(Field of View,视场角,VR 设备的重要指标之一,反映视野广度),4K 全景视频在 VR 眼镜上看起来也就只相当于 540P,所以 8K 分辨率视频的分发也仅仅是超高清画质体验的“入门级需求”。另外,一些游戏、体育赛事等内容的视频对帧率也有很高的要求,达到 120fps 才会有较好的体验;传输方面,要实现对这类「富媒体」的超低延时传输则是个很大的挑战,带宽需达到 150Mbps 以上。
VR 眼镜方面,最近两年 VR 一体机技术发展迅速,它 All-in-one 的设计脱离了外部设备的连线束缚,即开即用,受到了市场的广泛欢迎,有逐渐代替 VR 头显之势。不过,“便携”的优点也不可避免地会影响它在解码、渲染、带宽处理上的性能表现,在处理上述 8K@120fps / 150Mbps 的任务时需要进行特殊处理。
当前行业使用的一些解决方案在视频质量/帧率/延时/带宽等各方面做了取舍,导致最终用户体验不太理想:要么是无法忍受的图像质量(低画质),或者是低帧率带来的眩晕(低帧率),又或是无法忍受的延时(高延时),以及巨额的带宽成本(最后一公里全景下发)等,像业内采用的「直播转码」+ 「CDN 分发链路」方案,一方面它的延时较高,无法适用于一些互动性较高的场景;另一方面,由于在云端进行了一次转码,对画质会产生一定的损伤,也会影响用户的“沉浸式”体验。
利用 RTC 传输这类「富媒体」到 VR 一体机可以较好地解决高画质和低延时的问题,但也面临着一些难点。
上文已提到,在 VR 场景中,像云游戏、大型展会、赛事等内容的视频,「高分辨率」和「高帧率」缺一不可。然而我们发现,不管是 GPU 还是 VR 一体机的芯片,其编解码能力都无法兼顾到「8K」和「120 fps」性能体验。我们使用了 gpu-z 工具和 Nsight 工具分析了 Nvidia Tesla 硬件的编码能力,分析发现,当视频源达到 8K 分辨率时,单张 Nvidia Tesla 最高只能支持到 8K@60fps,且存在性能波动,一般单张显卡的性能稳定在 8K@50fps。
以下为测试数据:

从解码能力看,目前市场上主流的 VR 一体机(价位1500-2000元)基本都选用 高通 XR2 芯片,该芯片对外宣称的解码能力为 8K@60fps 或 4k@120fps,实测下来发现,8K@60fps 也是上限数值,实际难以稳定在 8K@60fps。
以下为测试数据:

因此,编解码的性能成为了支持 8K@120fps 最大的瓶颈。
传输 8K@120fps 全景视频需要 150Mbps 的带宽,目前 5G 渗透率还不高,宽带下载网速无法满足这样的传输条件。
以下为三大运营商2021年下行速度中值数据:

数据来源:《2021年全国网络速度和质量报告》
从合理性上看,VR 眼镜由于视角问题,观看端并不需要同时解码全场景的画面内容,全解码方案浪费了大部分的码流带宽占用,造成了很大的下行带宽,给最后一公里带来了巨大的压力,不利于互联网分发。

MTP(Motion To Photons)是 VR 眼镜的另一个重要指标,指从头动到显示出相应画面的时间,MTP 时延太大容易引起眩晕,目前公认的是,当 MTP 时延低于 20ms 就能大幅减少晕动症的发生。
为了解决上述难题,火山引擎 RTC 引入了 FoV 方案,即让接收端只接收视角区域内的高清码流来解决编解码性能不足和带宽不足的问题。另外,我们通过同时传输高清的 tile 码流和低清的全景背景码流,避免因快速头动导致视角切换而引起的黑屏。利用火山引擎覆盖全球的实时音视频网络边缘节点,最终可实现低清背景 MTP < 20ms,高清 FoV 流 MTP < 100ms。

如图所示,首先,编码端将一路 8K 视频划分成若干个 tile(在 HEVC中,从水平和垂直方向将图像分割为若干个矩形区域,把这些矩形区域称为 tile,每个 tile 都可以独立编码解码),对每个 tile 使用自研编码器单独进行编码;同时编码一个 2K 的全景图,它可以在接收端做“兜底”,又不会引入较大的码率增加导致解码端性能跟不上;然后,在媒体服务器侧,上行通过一个 ssrc 同时接收高清 tile 流和低清背景流,其中下行高清 tile 流按照用户视场角过滤转发,下行低清背景流不过滤直接全部转发;最后,接收端按照 HEVC tile 标准,将所有 tile 按照图像的位置合并成一路原始大小的编码视频,解码,上屏。
下文详细介绍火山引擎 RTC FoV 方案的实现与优化。
上文提到,由于单张 Nvidia Tesla 不具备 8K@120fps 的编码能力,所以需要通过多 GPU 并行编码来实现。火山引擎 RTC 在编码侧采用多 Nvidia Tesla 显卡并行,将 8K 视频切割成若干个 tile,使用若干个编码器进行编码,然后通过 RTP 打包发送到网络。

这里需要注意的是不是所有的显卡都能创建多个硬编码器,个人消费级显卡对于编码器的个数是有限制的,Nvidia 的显卡可以在官网进行查询。
码率的准确性对下行可接入的 VR 一体机数量比较重要,但测试中我们发现编码器码率控制有时会不准,且单纯调节编码器的编码参数并不能解决这个问题,于是需要在硬编码器内部定时对编码器的实际编码码率进行监控,监控频度设置为 10s,如果实际编码低于预期码率则统一调高所有编码器的码率,反之则调低,调整粒度为 10%。经测试,增加码率监控后能够稳定码率为预设码率。
编码器的复杂度在默认情况下是在创建完成编码器的时候确认好的,中间不能动态修改,这样会存在如下问题:
编码器的复杂度可以通过 preset 来划分,不同的 preset 表示了不同的复杂度(对于 preset 的详细说明可参考 Nvidia 官网的资料),我们实测数据如下:

通过测试数据,我们发现 preset p1 和 p4 是两个性能临界,可以通过动态调整 preset 来提升编码复杂度,进而提升编码的画质(preset 的动态设置耗时不大,不会导致画面卡顿)。因此,我们将当前默认设置的 preset 调整为 p4,如果 p4 性能不能保障实时性,则回退到 p1。
一些直播场景中已经开始使用 FoV 方案,但目前还没有 RTC 厂商来按视场角下发视频内容。
为什么不用 SVC 或 Simulcast 做视频下发?SVC 和 Simulcast 只能针对视频全画幅进行接收和解码,会引起带宽的增加或画质的损失。而火山引擎的 FoV 方案中,一路高清视频流按视角场异步下发和渲染,一路低清视频流全量下发,既可以节省带宽,也没有降低画质,还能避免因视角快速切换、高清视频来不及传输导致看不到画面。
球面和平面之间图像的映射问题,是一个从古时候起就一直困扰着地图测绘员的问题。今天,随着 VR 全景视频的发展,又将这一问题摆在了开发者面前。VR 全景视频需要传输,涉及到带宽占用和画质损伤的问题, 不同的投影方式会对画质及码率造成较大的影响。

我们使用了 EAC 的投影方式,相对于简单直观的 ERP 投影,EAC 投影比 ERP 投影节省了 25% 的面积,接收端降低约 15% 的数据接收,且更利于视频编码器做画质优化。
下面两组照片中,上图为 ERP 投影,像素为 7680x3840 ;下图为 EAC 投影,像素仅为 5760x3840。



定义正前方是零点向量,视场角边界是 tile 向量,零点向量和 tile 向量夹角小于 X° 范围内的 tile,都是视场角范围内的 tile。
如上图所示,粉色+黄色是全景的视频,划分成了若干个 640x640 的区域,黄色区域是根据向量夹角计算出来的视场角范围内的 tile,然后接收端向 RTC 边缘媒体服务器请求,下发这些 tile。
接收端按照 HEVC tile 标准,将所有 tile 按照图像的位置合并成一路原始大小的编码视频;同时,将 2K 低清流进行放大,并将高清 FoV 流在渲染前贴到对应的坐标位置。

放大后效果如上图,橙色部分为低清流,放大成为 8K;绿色部分为高清 FoV 流,为原始的 8K。
如果头动较慢,VR 头显中看到的都是高清的视野范围,所以不会对实际体验造成影响;如果产生快速的头动,那就无法避免在视野范围内看到一些低清的图像,此时播放端会根据头动范围重新请求高清 FoV 码流,此时会有短暂的时间看到低清图像,等到高清 FoV 范围的码流下发下来之后,画面就会恢复高清 8K 效果。
头动延时 = 最后一公里网络rtt + GOP/2 + jitter_buffer + 解码 + 上屏
下图说明了“视场角预测”的流程,即,用户当前 FoV -> 转头 -> 控制信令(携带预测结果) -> RTC 边缘媒体服务器 -> 下发新的 tile -> 更新 FoV 内容。

行业中已经有一些比较成熟的视角预测方案,当用户头部旋转时,可以根据旋转加速度进行预测未来旋转的角度位置,甚至可以根据用户的动作预测转动角度和方向,再根据预测进行拉取相应数据,可以达到很好的预判以及降低延时效果。
首先,这里仅采用本用户自身的历史数据来预测其未来视角,其次,为了适应用户的较快速头动模式,选择了速度较快的 ML 算法来预测。
上述方案在实际落地中的表现如下:
在 GOP=15 的情况下,8K 高清头动延时在 100ms,端到端延时为 130ms+,下行码率约 20Mbps,数据表现理想。

实际体验效果如下:
注:1、为了表现高清 FoV 视频和低清背景视频的区别,我们给低清视频添加了绿色滤镜
当头动速度较慢时,视场角范围内只能看到高清的图,看不到绿色的低清图。
当头动速到较快时,才会偶尔有绿色的低清 tile 块进入到视场角范围内(想象一下,如果没有低清视频流兜底,用户看到的将是缺失的画面)。 [视频请跳转此链接](基于 RTC 的全景 8K@120fps FoV 实践)
火山引擎 RTC FoV 方案通过如下的技术优化,实现了 8K@120fps 全景视频的实时传输:
当前方案仍有不少优化空间。
比如当前在解码端将 2K 低清背景流到放大到 8K 高清流的采用的是传统的缩放算法,会对画质造成一定的损失,使用超分算法会极大的提高低清背景的优化体验。
AI 头动预测,利用多个用户的头动数据学习得到具有群体共性的头动模式,从而能在未来一段时间内加快内容预取,进行预测。
另外,目前 Nvidia 和高通主流芯片平台均已支持 HDR 10 的编码和解码 (High-Dynamic Range,是一种提高影像亮度和对比度的处理技术,它可以将每个暗部的细节变亮,暗的地方更暗,丰富更多细节色彩) ,我们后续也将引入 HDR 10 技术来进一步提升画质体验,让用户更接近真实环境中的视觉感受。
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源
参见下面的示例,我想最好使用第二种方法,但第一种也可以。哪种方法最好,使用另一种的后果是什么?classTestdefstartp"started"endtest=Test.newtest.startendclassTest2defstartp"started"endendtest2=Test2.newtest2.start 最佳答案 我肯定会说第二种变体更有意义。第一个不会导致错误,但对象实例化完全过时且毫无意义。外部变量在类的范围内不可见:var="string"classAvar=A.newendputsvar#=>strin
我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n
如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。