流媒体服务从 RTMP 迁移到 WebRTC
一个视频流项目的历史
一个有趣的客户
我盯着显示器已经有一个小时了,或者可能两个小时了。一切都始于我同事在 Skype 上好意分享的一个 Twitter 链接。然后,我偶尔打开了一个新闻网站,然后是 Facebook,等我再看到一些值得关注的新闻时,已经过去了相当长的时间……
总之,我的背部已经麻木了,我决定出去走走,伸展一下。在夏日的炎热中烤自己可不是个好主意——办公室里有空调默默地工作,凉爽宜人。所以,我一瘸一拐地走到了最近的咖啡机。
接待处响起了门铃。几分钟后,我看到安娜陪着一位高个子绅士走了过来。我估计他五十岁左右。他头上戴着一顶略显皱纹的灰色短边帽。他们正朝我走来。
走到咖啡机旁,咖啡机已经往我的杯子里倒了卡布奇诺,这位绅士用略带口音的英语说——你好,我来谈 WebRTC 项目。我叫 Hans Schreder,然后他伸出了手。我握着他的手,心里想着是什么把这位德国人带到了这里,然后邀请客人到我的办公室。
收集需求
H:我们自 2000 年以来一直为大量用户提供流媒体和 Flex 服务。我们使用 Adobe Flash Media Server (FMS),并希望切换到 WebRTC。
我:您能否详细说明一下您希望通过切换到 WebRTC 服务器实现什么目标?
H:我们需要一个标准的媒体服务器,它能接收用户的视频流并将其传输给其他用户。我们想要一个视频聊天。
我:没问题,我们可以基于一个 WebRTC 服务器开发解决方案。
H:Adobe FMS 对我们来说运行良好。我们只是想将用户群扩展到 WebRTC,同时保持 FMS 不变。它运行得很好。 Hans 拿出一个平板电脑,递给我,并指向以下图表
H:Flex 应用是医生,Flex 应用是患者。医生使用网络摄像头同时咨询多位患者。其中一位患者可能需要私人咨询,医生会为该患者发起一对一的私人预约。其他患者在此期间不能与医生交流,也无法看到他。
我心想,这有点奇怪。医生能给多个病人提供什么样的咨询呢?一个耳朵疼,另一个抱怨扁桃体发炎。然后,那个扁桃体发炎的病人点击一个按钮进行私人咨询。不过,原理是清楚的。我们来谈谈技术方面的问题。
我:您的意思是医生和病人之间的视频连接是双向的吗?
H:不完全是。病人总是能看到医生,但医生看不到病人。默认情况下,病人的视频是关闭的。
我:那么,它是单向的——从医生到病人?
H:在很多情况下是这样的。但是,有时候病人也想给医生看他们的视频。不过这种情况很少见。
我:我明白了。那么,您希望医生和病人都使用 WebRTC 浏览器,比如 Firefox 或 Google Chrome,以及通过 FMS 工作的 IE,是这样吗?
H:差不多。我们所有的医生都使用我们开发的 Flex 应用。病人也应该使用这个应用,或者 WebRTC。
我:因此,理想情况下,应用程序应该看起来像这样?我画了一个示意图。
H:是的,应该就是这样。一边是原生的 Flex 应用程序,另一边是 WebRTC 浏览器。我们主要对 Android 智能手机和 iOS 设备感兴趣。正如您所知,Flash 支持所有主要的桌面浏览器:IE、Chrome、Firefox、Safari,但 Android 和 iOS 缺乏支持。我们希望您也能让我们的服务支持移动用户,并保留桌面计算机上有效的功能,即 FMS。
我:WebRTC 在 Android 浏览器上运行良好,但我们在 iOS 上遇到了问题——由于平台限制,WebRTC 在 iOS 上无法运行。我们无法向 iOS 提供 WebRTC 视频流,也无法从 iOS 浏览器捕获网络摄像头视频。
H:等一下,我知道 Safari 不支持 WebRTC,但 Google Chrome 支持。
我:是的,但在 iOS 上不行。在此平台上,Chrome 由于平台的限制而受阻,并且确实无法实现与桌面计算机类似的 WebRTC 视频功能。也就是说,iOS 浏览器在这里不可行。你们为什么不考虑将自己的应用上传到 Apple App Store 呢?这样,iOS 用户就可以简单地安装应用,并像 Google Chrome 一样使用纯 WebRTC 解决方案了?
H:很遗憾,由于一些特殊原因,我们无法将我们的应用程序提交到 App Store。此外,我们希望用户(病人)能够直接从浏览器与医生交流,而无需在他们的 iPhone 或 iPad 上安装任何应用。那么我们还有什么选择呢?
这时我想到了一些阻止他们发布应用程序到 App Store 的“特殊原因”。确实,医疗咨询可能是一个受监管的领域,所以简单地将此类应用程序上传到 App Store 可能并不那么容易。
事实上,剩下的选择并不多。对他们来说最好的选择是支持 WebRTC 的原生应用程序。众所周知,iOS Safari 支持 HLS(Apple HTTP Live Streaming),但由于医生和病人之间假设的实时对话,此选项已被拒绝。而 HLS 延迟高达 20 秒,根本不适合实时通信。
唯一剩下的选择是 WebSockets。几乎所有浏览器都支持 WebSockets。基本上,它是一个 TCP 通道,以低延迟传输视频,延迟约为 3 秒,这与 RTMP 相当。总而言之,这比 20 秒要好得多。这是关于传输。我们还想将这个流播放到 HTML5 的 'video' 元素中,然后就可以完成了。
我:看来我们只有一个选择了——WebSockets。在这种情况下,病人将无法将他们的视频发送到服务器。只能实现从医生到病人的单向传输。我们也可以尝试 HLS,但延迟约 20 秒,您很难认为这个选项合适。
H:好的。我是否理解正确,我们可以直接在 iOS Safari 浏览器中播放 FMS 的实时流?无论是否 WebRTC,但仍然具有类似 RTMP 的低延迟?
我:绝对可以。但我们必须先进行测试。我们约定周一,我会向您展示演示。
H:我想看看 FMS 如何与 WebRTC 和 WebSockets 集成,以确保它能在 iOS 和 Android 上都运行。这可能吗?
我:是的,我认为这是可能的。
H:感谢您的咨询。如果您不介意,我周一上午 10 点过来,届时我们已经有了正在运行的演示。
我:当然。届时一切都会准备就绪。
寻找解决方案
从谈话可以看出,需求已经发生了变化。现在我们需要连接到 Adobe AMS 的传输方法:Android 用 WebRTC,iOS Safari 用 WebSockets。现在,我们只需要找到那个能让我们构建演示并合并所有涉及的协议和技术的缺失环节。
我送走了这位德国客人,开始查看 Adobe AMS 规范。那里有很多有趣的东西,但没有“WebRTC”和“WebSocket”这两个词。
然后,我决定简单地用三个关键词搜索:rtmp、webrtc、websockets。Google 返回了一系列相关网站。其中只有两个值得一看:一个项目 Flashphoner 和一个开源原型 Phoboslab 的描述。
第一个候选
我从 Phoboslab 开始,在那里我找到了关于 iOS Safari 视频流播放问题的技术描述和一个开源的解决方案。该解决方案基于 ffmpeg、node.js 和一个用于解码和播放视频流的客户端 JavaScript。所有组件确实都是开源的,所以这个方案看起来很有希望。我在 DO 上设置了一个虚拟服务器,构建了 ffmpeg 并安装了 node.js。总共只花了两个小时。
视频确实在 iOS Safari 中播放了,而且效果很好。我的 iPhone5 有点发热,但 JavaScript 稳定地处理了来自 Websocket 的视频流量并将其显示在网页上。
事实上,JavaScript 解码了视频流并将其绘制在 iOS Safari 浏览器中页面的 'canvas' 元素上。
然而,接下来的问题仍然存在
- 如何从 FMS 获取流
- 如何为流添加声音
- WebRTC 呢?
这时我感到有些沮丧。事实证明,JavaScript 播放器只播放(绘制)视频。对于音频,我们需要一个额外的流并以某种方式同步它们。但这个解决方案并没有为此设计。因此,这个候选者不符合要求。由于缺乏声音,它无法传输医生的视频。
第二个候选
另一个对象是 Web Call Server,它声称支持 RTMP、WebRTC、WebSocket 协议。所以,我只需要测试它是否适用于我的特定情况,并看看它是如何工作的。
首先,我决定测试 RTMP 视频流如何转换为 WebSocket,就像我之前对第一个候选者所做的那样。如果我成功了,我们就可以简单地将 FMS 的 RTMP 流重定向到 Web Call Server,从而解决其中一个任务。
我带着我的 iPhone,打开了一个演示页面,该页面提供了从演示服务器测试操作的功能。据支持人员介绍,Web Call Server 可以快速安装在 Linux 系统上,但这仍然需要时间,而演示是一种快速查看它是否有效的方法。演示界面是一个简单的 Flash 应用程序,称为 Flash Streaming,界面简洁,功能非常简单。
从这个 Flash 应用中,可以通过 RTMP 协议连接到服务器,并发布网络摄像头流。发布意味着使用 Flash Player 从浏览器的网络摄像头捕获视频流,并使用 RTMP 协议实时将数据发送到服务器。
根据“已连接”和“正在发布”状态,连接成功,并且网络摄像头流已正确发送到服务器。为了避免在流中出现我自己的脸,我使用了虚拟摄像头,并播放了一集随机的《权力的游戏》。
现在,我们需要在 Safari 浏览器中的 iPhone 上查看和听到视频流。为此,推荐了一个单独的播放器,称为 WS Player Minimal。
播放器能够显示清晰的图像和声音,没有失真或不同步。看来我在这个方面取得了一些进展。
- 我成功测试了 RTMP-WebSocket 流传输
- 流包含声音和视频,并在 Safari 中正确显示
我需要测试该流的 WebRTC 播放,并切换到 Adobe FMS 集成。为了用 WebRTC 测试相同的流,我在 Chrome 中打开了另一个名为 Streamer and Player Minimal 的演示,并完成了相同的简单过程:粘贴流名称并点击播放。
天哪,我太高兴了,龙母!
现在,我的武器库中有将 RTMP 流传输到 Chrome 的能力,这意味着也可以通过 WebRTC 将其传输到 Android,通过 WebSockets 传输到 iOS Safari。在这两种情况下,图像都很流畅,包含声音,并且非常适合咨询服务。
接下来的任务是处理 FMS。当然,RTMP 协议应该对所有实现都相同,但我必须弄清楚 a) FMS 是否可以将 RTMP 流重新传输到 Flashphoner,以及 b) 这个 Flashphoner 是否能像在上面的测试中一样接受这个流。
Adobe Media Server 集成
与 FMS 打交道花了一些力气。安装和测试花了几个小时。我首先做的是用 FML?测试 FMS,并确保我正确安装和配置了 FMS,并且 RTMP 视频流可以无障碍地运行。
下一步是配置将 RTMP 流重定向到 Flashphoner。在这里,我不得不费尽心思。在我查阅了 Adobe Action Script 文档后,我最终实现了以下脚本:main.asc
var wcsServer = "wcs5-eu.flashphoner.com";
var netConnections = new Object();
var streams = new Object();
application.onConnect = function (client){
trace("onConnect "+client.id);
var nc = new NetConnection();
var obj = new Object();
obj.login = "Alice";
obj.appKey = "flashChatApp";
nc.connect("rtmp://"+wcsServer+":1935",obj);
nc.onStatus = function(info){
trace("onStatus info.code: "+info.code);
if (info.code=="NetConnection.Connect.Success"){
trace("connection opened "+wcsServer);
}
}
netConnections[client.id]=nc;
return true;
}
application.onDisconnect = function (client){
trace("onDisconnect "+client.id);
var nc = netConnections[client.id];
if (nc){
nc.close();
trace("disconnected "+client.id);
}
}
application.onPublish = function(client, myStream){
trace("onPublish "+myStream.name);
var nc = netConnections[client.id];
ns = new NetStream(nc);
ns.onStatus = function(info){
if (info.code == "NetStream.Publish.Start"){
trace("It is now publishing "+myStream.name);
}
}
ns.attach(myStream);
ns.publish(myStream.name);
streams[myStream.name]=ns;
trace("publish stream "+myStream.name+" to: "+wcsServer);
}
application.onUnpublish = function(client, myStream){
trace("onUnpublish "+myStream.name);
var ns = streams[myStream.name];
if (ns){
ns.publish(false);
trace("unpublished "+ns.name);
}
}
这个脚本很简单。它所做的就是将所有传入的 FMS 连接和视频流委托给 Flashphoner 服务器。例如,如果在 onConnect 方法中收到来自应用程序的连接,就会建立到 Flashphoner 的 RTMP 连接。如果在 onPublish 中收到视频流,则相同的视频流会在 Flashphoner 上发布。在任何断开连接或停止的流的情况下,都会将相应的调用委托出去以释放占用的资源。
因此,我在 FMS 和 Flashphoner 之间建立了一个桥梁,用于传输流量,然后将其投射到 WebRTC 和 WebSockets。
为了测试这个组合,我使用了之前用过的 Flash 应用程序 Flash Streaming 界面。现在唯一的区别是指定 FMS 服务器的 RTMP 地址,并依靠 main.asc 脚本,该脚本应该将此视频流委托给 Flashphoner。在我的例子中,地址是 rtmp://my-fms:1935
由于我对 Action Script 的不熟悉和糟糕的 FMS 编程技能,我在调试这个脚本时经历了九死一生的痛苦,但过去已成过去,上面的代码示例是最终可用的脚本版本。FMS 工作良好,并按预期将 RTMP 流传递给了接收方,这使我能够在 Chrome 和 Safari 中成功播放它。
安装 Web Call Server
所以演示就准备好了。唯一剩下要做的就是将 Web Call Server 安装到我自己的系统上,以防止演示过程中出现任何故障。谁知道他们会在周一之前改变什么?开发人员网站提供了只有 5 个步骤的安装说明。我省略了第五步——使用 SSL 证书进行安装,因为我无意使用网络摄像头和麦克风的 WebRTC 流。
- 下载 Web Call Server 5
- 使用 'install.sh' 脚本进行安装
- 使用 'service webcallserver start' 启动
- 打开 Web 界面 http://host:9091 并激活您的许可证
安装成功。我预先禁用了测试服务器上的防火墙,执行了 "service iptables stop",以避免任何可能的流量锁定问题。安装服务器一分钟后,我成功地用管理面板打开了 Web 界面 http://host:9091,激活了测试许可证,并在我的 Ubuntu 上得到了一个演示服务器,看起来非常像这样。
又一次测试让我确信,在我的环境中系统也能完美运行。带着成就感,我完成了工作,并给自己留下一条便条,要在周一早上 9:00 再次运行测试,就在 Hans 来之前。第一部分就到这里。如果有人感兴趣,我将在第二部分描述迁移问题以及最终结果。
使用的工具
- FMS (Flash Media Server) 又名 AMS (Adobe Media Server)?RTMP 媒体服务器。
- DO (Digital Ocean)?虚拟服务器托管。
- WCS (Flashphoner Web Call Server)?WebRTC、WebSocket 媒体服务器。
- FMLE (Adobe Flash Media Live Encoder)?一个用于检查服务器 RTMP 连接的客户端。
- Phoboslab - 一个用于向 iOS Safari 进行 WebSocket 流传输的开源原型。