Files
monibuca/dashboard/docs/design.md
2020-02-03 21:30:41 +08:00

4.9 KiB
Raw Permalink Blame History

Monibuca设计思想

背景

市面上的流媒体服务器不可谓不多,从本人的第一份工作起,就一直接触和研究了形形色色的流媒体服务器,从最早的FCS(全称Flash Communication Server),后来改名为FMS(全称Flash Media Server),到Red5(java语言开发),到CrtmpServer(C++开发)让我对流媒体服务器的基本原理有了深刻的认识。当时本人痴迷C#于是乎在业余时间对crtmpServer的代码进行移植用C#仿照着写了一遍取名为csharprtmp并且适当的增强了一些功能于是对rtmp协议了如指掌。后来Adobe推出了RTMFP协议是一种p2p协议十分节省带宽。我就又开始研究一款名为OpenRTMFP的开源项目,后来该项目改名为MonaServer。我在起基础上进行了扩展实现了一些例如录制flvshareObject等原本FMS有的功能。后开发出了HTML5直播技术现在命名为Jessibuca,尚未开源)采用的传输协议就是WebSocket传输裸的视频流的方式属于私有协议。而Server当时就使用的MonaServer。但当时遇到一个问题C++的内存泄漏问题这个一直没有很好的解决。遂决定放弃使用MonaServer转而使用srs而srs要用一个很简单的go写的小程序将http-flv转换成WebSocket的Flv来适配我的Jessibuca感觉最好能直接修改srs来实现这个功能。对srs的源码研究了一小段时间后放弃了因为C++代码过于难写容易出现bug。后来转而使用golang写的gortmp作为server同样对其进行了扩展而且进展十分顺利golang的开发效率令人惊叹而且其协程的特性很完美的处理了流媒体服务器的并发的场景。所以使用golang写的流媒体服务器项目很多github上随便一搜就有很多比如livegojoy4等。期间还接触到一位使用Node.js实现的流媒体服务器Node Media Server我也和作者交流了许多收益良多。

现有项目的不足

虽然流媒体服务器项目很多,但在我使用过程中遇到了几个痛点

  1. 功能太多太重,往往大而全,不够轻量 很多号称轻量的项目最后都会越来越重
  2. 扩展性弱由于功能复杂设计之初没有提供良好的扩展性有些项目带有脚本支持如FMS和MonaServer但执行脚本会牺牲性能而且脚本和原生代码相比功能限制很大只能实现业务逻辑而不是流媒体服务器本身的功能扩展。
  3. 缺少图形管理界面FMS是配套有图形管理界面的当然FMS的问题是商业软件需要付费源码也是不可见的。

综上所述本人在吸收了以上诸多流媒体服务器的设计后完成了Monibuca这款golang编写的流媒体开发框架的编写

受到vue渐进式思想的影响

vue渐进式框架的设计思想非常棒那么是否可以用来设计流媒体服务器使得流媒体服务器不只是一个服务器而是一个开发框架让开发者可以定制化自己的流媒体服务器呢答案是肯定的。当然我们需要更多的抽象。

流媒体服务器的核心

流媒体服务器的核心是转发二字。当你去研究一款流媒体服务器的时候,会有海量的代码阻碍你看清其核心逻辑。包括:

  1. 多媒体格式定义、解析如Flv、MP4、MP3、H264、AAC等等
  2. 传输协议的解析如RTMP家族、AMF、HTTP、RTSP、HLS、WebSocket等等
  3. 各种工具类,用来读取字节的缓冲、大小端转换、加解密算法、等等

大部分流媒体服务器都是基于rtmp协议之上扩展而来这是历史原因造成的所以功能不能很好的分离耦合度很高。往往牵一发而动全身。其实所谓的流媒体服务器本质上就是把发布者的数据经过服务器转发到订阅者手里播放起一个中转作用。至于什么协议格式什么媒体格式都是属于扩展功能。所以最轻量的服务器应该不包含任何协议格式任何媒体格式仅仅只是完成中转。再说的直白一点核心代码就是一个for循环。

for _, v := range r.Subscribers {
    v.sendVideo(video)
}

其他都是围绕这个for循环展开。所有的流媒体服务器代码里面都有这个for循环写法稍有不同但本质相同。

核心概念

基于这个循环,我们需要思考两个问题:

  1. 如何高效的循环(性能问题)
  2. 如何对接不同的协议(扩展性)

第一个问题golang的性能算是很好的那么重点就在于减少内存的分配上池化是一个不错的选择所以尽量池化在Monibuca中对[]byte类型,采用了github.com/funny/slab包来管理。其他结构体就用系统自带的pool包来池化对象。对于协程的使用在多次迭代后已经使用了最少的协程来支持并发性。