大家可能还记得几个月前我写了一篇博客,介绍了我做的一次酷炫尝试——使用新的Janus插件,以一种 “简单”方式来连通WebRTC和NDI世界,借此来解决现今广播行业存在的问题。文章一经发出,就引发了很多广播电视业界人士的兴趣。此后我也在几个不同的场合详细介绍了我的这次尝试。比如之前举办的ClueCon Dangerous Demos会议,参与者来自世界各地,我也有幸直播演示了Janus NDI插件的使用方法。再比如在和Arin Sime的聊天中,我也很荣幸能更详细地介绍了我的这次尝试。
当然,我们也没有就此停滞不前。我们注意到了很多人对该尝试表现出的巨大兴趣,并且开始与Dan Jenkins和Nimble Ape建立合作关系。
在向大家解决什么是broadcaster.vc,以及它的工作流程之前,让我们先来看看Janus NDI插件经历的一些变化,以及我们做了哪些努力使这个酷炫的尝试变成更可靠和可用的现实。
目前我们处于哪个阶段?
简而言之,NDI插件被预设为能够通过Janus接收WebRTC流,并将其实时转化为NDI。这里就产生了第一个问题:如果我们想给任意远程参与者之间的对话接收NDI源,使用怎样的拓扑结构才算正确呢?这让我们想到了下图场景中所描述的方法,其中两个独立的Janus实例实际上是场景的一部分,包括:
1. 一个公共Janus实例,服务于远程参与者之间的对话本身(例如借VideoRoom的 SFU插件实现的实例);
2. 一个私有Janus实例,托管NDI插件,与NDI消费者在同一个局域网内,配置为接收来自公共实例的视频。
正如我们后面会看到的那样,这种方法实际上并没有改变broadcaster.vc的预设基础。也就是说在之前编写的NDI插件真正投入使用之前,仍有一些事情需要我们解决。
改进插件
事实上,作为一个主要的演示或POC来说,该插件有一些关键限制需要解决。其中一个重要的限制是:音频和视频流一经收到就被解码了。当一切都在本地运行时,这无伤大雅。但当组件需要与在互联网上运行的应用程序进行交互时,这就是个大问题了。事实上,数据包丢失和失序的数据包显然让解码器无所适从。如下图(Dan的房子看起来像着火了一样)。
在插件中添加一个简单的可配置的抖动缓冲区,这个问题就能迎刃而解了,这样也以便在实际解码之前“加速”输入流量。虽然这给处理过程增加了一点小延迟,但无论如何,所有RTC应用程序都是这么处理音频和视频流的,对NDI源的影响可以忽略不计。另外我们还增加了对RTCP PLI消息的支持,以应对尽管有缓冲区,但视频解码仍然可能导致错误的一些情况。
另一个限制问题是编解码器支持。事实上为了保持简约,最初的Janus NDI插件只支持VP8视频。对于一个简单的演示来说,这么做是有意义的,但对于旨在和外界异构WebRTC应用进行交互的组件来说,这么做意义不大。因此我们增加了对VP9和H.264的支持,这意味着我们在将有效载荷送达解码器之前增加了正确解包的方法。其实我们也有H.265和AV1的代码,但由于它们还不是在全球范围内可用,我们决定暂时不采用它们。
在音频方面我们也进行了一些整改。虽然我们没有添加任何其他的编解码器 (如Monkey Island所说:”只有一个候选人, 意味着只有一个选择”), 但我们确实添加了对立体声流的支持, 我们认为对广播公司来说这很有用。此外, 双声道比OBS酷多了!
视频分辨率一直在变!
对广播公司来说,这应该是他们处理NDI流时最讨厌的事情之一了。
了解WebRTC的人应该知道在WebRTC中这种情景见怪不怪了。在你极力以特定的分辨率捕捉视频时,浏览器可能会因为突发情况(比如来自网络和RTCP协议栈的反馈,或者比如CPU占用率)随时决定缩小/扩大视频。
这其实并不是一件坏事,相反,是一种节省资源的好方法。不过,对于那些处理传入视频的人来说,如果他们没有做好准备,或者他们更愿意使用一致的分辨率(例如,出于录制的目的),视频分辨率变动会是很大的问题,对基于NDI的我们来说也会产生一些麻烦。事实上,最初的Janus NDI插件将WebRTC流翻译成NDI流,使用同一精确分辨率。这意味着WebRTC sude上分辨率的变化也会通过NDI反映出来。
虽然一些NDI用户有方法可以将传入流的渲染“锁定”为特定的分辨率(OBS可以做到这一点),我们认为在我们的插件中选择性地配置一个机制来强制执行这种一致性也是可行的。即为NDI流提供一个目标宽度/高度,且无论实际分辨率如何,总是将传入的视频缩放到这个标准。
这可能是一个非常受欢迎的功能,但需要注意的是它并不能解决所有情况下的问题。事实上,对于来自网络摄像头的视频流,你可以安全地假设一个固定的分辨率作为参考(检查MediaStream轨道并在插件中使用该信息很容易),但对于屏幕共享会话,情况就完全不同了。因为人们可能会共享应用程序,并在整个会话中随机调整窗口的大小。在我写这篇文章的时候,NDI插件并没有被设置考虑长宽比。也就是说,如果通过插件API强制缩放,传入的视频可能会因为目标分辨率的比例不匹配被拉伸(应该是在应用共享和调整窗口大小时)。我们下一步计划在实际中考虑长宽比,以便缩放后的视频“适配”目标帧,另外我们可能用一些背景或背景条填充剩余部分(还没有最终确定)。
水印
我想到另一个可实现的功能是自定义图形。如果人们想以某种方式给视频打上水印要怎么办?可以肯定的是,广播应用,也就是使用NDI流的那些应用里会有这种类似的操作。然而在某些情况下可能需要视频本身去硬编码。
如图所示,我们想到在插件中设计出一个简单水印功能。它目前的工作方式非常简单:你可以提供一个想使用图像的路径(本地文件或网络资源),指定它它应该被缩放的大小,最后确定它应该被 “画 “在视频上的哪个位置即可。如图,你可以看到Meetecho的logo水印很成功,不会遮盖我的脸。
很酷的是,一旦水印被创建并添加到视频中,你可以随意改变它的位置,例如,把它放在右上角,或者放在左下角。
但正如你想象的那样,考虑到大小和位置都是绝对的,目前只有当你强制执行静态分辨率时,该功能才会像我们在上图中所看到的那样按预期正常运行。事实上,因为视频帧可能会根据发送者的网络/CPU而改变大小,水印可能会不成比例或错位,这取决于分辨率是否与大小/坐标所设想的分辨率相匹配。
也就是说,我们认为这将是一个有改进空间但现行版本也很有用的功能。我们想利用它来做更多事。例如提供免费的插件演示版本,其在所有NDI视频上都会硬编码一个标志。
如何测试NDI是否真的有效?
这个话题也很有意思。不深究细节的话,NDI基本只在局域网内使用mDNS来执行发现功能,也就是说只有当双方能看到这些消息流转时,流才是可见的、可用的。因此,我们需要一种方法来快速测试搭载我们NDI插件的个人Janus实例是否能被同一网络中其他NDI应用看到,这样就不需要创建一个WebRTC-to-NDI会话了。
我们找到了一种简单的测试方法——即通过NDI发送静态测试模式的可触发功能。简而言之,我们在插件中添加了一个API方法。如果被调用,它会创建一个临时的NDI发送器,并定期向其发送一张静态图像以模拟30fps的视频流。这使得测试设置是否正常工作的操作变得很容易。只需开启测试模式,再观察你是否能在自己的NDI应用程序中看到图像即可。如果你能看见图片,那就意味着WebRTC-to-NDI会话也是正常工作的。下图显示了来自静态测试模式和WebRTC流的OBS NDI流(我的脸)。
Dan手机里这张图是不是也还可以?
哪里能下载该插件呢?
虽然这款插件目前还不可用,但很快就会和大家见面了! 正如介绍中所说,我们和Dan合作创建了一项服务,它可以让你通过WebRTC很容易地主持一场在线对话,并通过NDI实现各对话窗口在你的网络中随意拉动的操作。
如果你想知道broadcaster.vc是如何工作的,请仔细阅读如下简介:
1. broadcaster.vc会提供基于Janus的会议基础设施服务;
2. 创建一个账户,以及你想用来与线上参与者进行在线对话的房间;
3. 下载一个包含带有NDI插件的Janus实例的定制docker镜像,然后在网络中运行它;
4. 在线对话开启后,网络中的docker实例会自动订阅活动流,并将其转化为NDI流;
5. NDI应用就可以运行了!
这就是broadcaster.vc的工作原理,就像上述解释的这么简单! 当然,在其实际可用之前,我们还有一些小问题需要解决。但大体计划就是这样的。如果你对此很感兴趣(事实上有很多人都有这个想法),欢迎和Dan联系来获取一个演示。
如果你没有意愿订阅此服务,而是想在自己的服务里使用/协调Janus NDI插件,现在我们正计划申请可购买的许可证,为这类用户单独提供此类服务。具体细节我们还没有敲定(目前也没有任何关于broadcaster.vc的定价信息),但很快就会面世了。所以请大家继续关注我们。之前也说过,我们可能会提供一个内测版本,通过试用该版本,大家就能判断该服务是否能满足你的要求了。
希望大家喜欢这篇文章。到目前为止,这些开发工作都相当有趣,我们很高兴能创建这项服务,期待它的面世!
原文作者:Lorenzo Miniero