- ▦ 一. lalserver 简介
- ✒ lalserver 特性
- ✒ 协议转换支持情况
- ▦ 二. lalserver 安装、运行
- ✒ 方式 1, 源码编译安装
- ✒ 方式 2, Docker 镜像
- ✒ 方式 3, 下载编译好的二进制可执行文件
- ▦ 三. lalserver 使用
- ✒ 各协议推拉流 url 地址列表
- ✒ lalserver 配置文件
- ✒ lalserver HTTP 事件和接口
- ✒ lalserver 进阶
- ▦ 四. 重新认识 lal
- ✒ lal 的三层结构
- ✦ 其他 demo
- ✦ 流媒体协议栈库 package/library
- ✦ Golang 通用基础库-naza
- ✒ lalext
- ✦ WebRTC
- ✦ MCU 合流
- ▦ 五. 联系作者
lalserver是纯 Golang 开发的流媒体(直播音视频网络传输)服务器。目前已支持 RTMP, RTSP(RTP/RTCP), HLS, HTTP[S]/WebSocket[S]-FLV/TS 协议。
c/c++
开发服务处于同一水平见: 附录-协议间转换支持情况
lalserver 支持 3 种安装、运行方式:
下载源码:
git clone https://github.com/q191201771/lal.git
编译:
cd lal
export GO111MODULE=on && export GOPROXY=https://goproxy.cn,https://goproxy.io,direct
make
tips:
如果使用 GoLand 等 IDE 编译,那么编译lal/app/lalserver
目录即可。
如果没有安装 Go 编译器,可参考《 CentOS 或 macOS 安装 GoLang 》,windows 操作系统可自行上网搜索教程。
运行:
./bin/lalserver -c conf/lalserver.conf.json
注意,windows 平台将路径分隔符/
换成\
下载镜像:
docker pull q191201771/lal
运行镜像:
$docker run -it -p 1935:1935 -p 8080:8080 -p 4433:4433 -p 5544:5544 -p 8083:8083 -p 8084:8084 -p 30000-30100:30000-30100/udp q191201771/lal /lal/bin/lalserver -c /lal/conf/lalserver.conf.json
如果想构建自己的 Docker 镜像,可参考 附录-构建自己的 Docker 镜像
lal 提供linux/macos/windows
平台编译好的 lal 二进制可执行文件(zip 压缩包形式)。
下载地址见: 《 github lal 最新 release 版本页面》
下载好的文件按方式 1 运行,不再赘述。
lalserver 启动成功后,就可以开始使用了。
作为流媒体服务,最主要的功能就是流数据转发。比如:
使用 ffmpeg 推 rtmp 流:
ffmpeg -re -i demo.flv -c:a copy -c:v copy -f flv rtmp://127.0.0.1:1935/live/test110
使用 ffplay 拉 rtmp 流播放:
ffplay rtmp://127.0.0.1/live/test110
更多协议见: lalserver 各协议推拉流 url 地址列表
tips:
更多第三方客户端的使用方法见: 常见推拉流客户端信息汇总
如果你想进一步了解 lalserver 的功能,可以看看: lalserver 配置文件说明
通过 lalserver 提供的丰富的 HTTP 接口,业务方可以在保持 lalserver 独立运行的情况下,使用自身熟悉的语言,轻松定制符合自身逻辑的业务系统。
lal 项目中,除了/app/lalserver
这个比较核心的服务之外,在/app/demo
目录下还额外提供了一些小应用,比如推、拉流客户端,以及压测工具,流分析工具,lalserver 集群的调度示例程序等。
这些 demo 你既可以直接使用,又向你演示了 lal 所提供的协议栈如何使用。
了解更多请访问: Demo 简介
lal 中的协议栈都是独立的,与应用分层设计的。并且客户端和服务端的协议栈都有。
业务方可以在自身的应用中集成 lal 的协议栈 package 库。
lal 将非流媒体特有的通用基础库抽象在一个独立的 github repo naza中。
了解更多请访问: 《 naza github 地址》: https://github.com/q191201771/naza
站在巨人的肩膀上才能看的更远。lal 在另一个 github repo lalext中,将 lal 的代码与第三方的库结合,实现了更丰富的功能。
rtmp 转 WebRTC 的网关
了解更多请访问: 《 lalext github 地址》: https://github.com/q191201771/lalext
作者微信,扫码加好友:
欢迎任何技术和非技术的交流。
1
Kasumi20 2022-03-30 20:15:30 +08:00
绑定
|
2
KiseXu 2022-03-30 20:35:54 +08:00
厉害了👍
|
3
milzero 2022-03-30 23:14:07 +08:00
媒体处理的部分,用 Go 会不会 GC 的问题,不是很懂 Go ,纯好奇
|
4
hronro 2022-03-30 23:20:28 +08:00
@yilelu0509 #3 没看过源码,不过我猜大概率是调 ffmpeg ,直接用 Go 处理的可能性不大
|
7
weak 2022-03-31 02:46:47 +08:00 via iPhone
流弊
|
8
ampedee 2022-03-31 08:43:57 +08:00 via iPhone
牛逼👍不过建议文档的高可用部分改成生产可用,高可用一般是指分布式部署故障切换等等。
|
9
thinkingbullet 2022-03-31 09:14:54 +08:00
流弊的很呐
|
10
zhs227 2022-03-31 09:18:58 +08:00
利害了。这么多东西都是作者一个人写的吗
|
11
notokoy OP 感谢各位的支持与回复。
目前 Go 的 GC ,STW 已经很短了,几乎不影响业务。 有一些知名互联网公司已经在生产环境使用 Go 编写的流媒体服务了。 @yilelu0509 lal (以及内部的 lalserver )没有第三方依赖,不依赖 ffmpeg 也不直接或间接依赖任何第三方 Go 库。 流媒体部分主要做网络传输,协议转封装,存储等部分。并不是非用 ffmpeg 不可。 日志,内存管理,连接管理等等基础功能也全部都自实现。 后续涉及到音视频编解码部分,可能会在另外一个工程 lalext 中依赖 ffmpeg 。但是会做成另外的服务来扩展。用户可按需选用。 lal (以及 lalserver )会一直保持独立性,因为 lal 与同类型项目间的差异性目标是,易用性且完全自主可控(包括代码层面以及开发环境、部署等方面) @hronro @yilelu0509 @dcoder lal 中的 lalserver 配合调度服务可以支持分布式集群,故障切换的。所以也算是高可用吧 :) @ampedee 现在代码基本是我一个人写的,但主体已经基本成型,现在开始推广并希望多吸收一些开发者参与。 流媒体方向的同学直接加入一起卷吧 :) @zhs227 |
12
lesismal 2022-03-31 11:19:11 +08:00 1
并发量大的时候,基于标准库的 net.Conn 还是消耗很大。7 、8 年前记得国外有个团队单机 50w 连接推流,内存、调度成本还是很高
去年我自己的异步框架功能基本完善后,也琢磨支持下流媒体的然后不用每个连接一个协程去循环读了,可以节省太多协程数量、内存、gc ,都可以变得可控,但是我自己暂时没那么多时间精力搞这一大套、得看以后有没有适合的时机了。如果作者有兴趣可以试下搞套异步的 PS ,阿里系开源真是各种 KPI 烂尾,之前看他们有人出了个 livego ,代码感人,低级错误、连 panic 都没处理好稍微测试了下就宕机了,本想 pr 修复下,但是看已经烂尾了所以没再关注了 楼主这个,我之前就有关注过,加油! |
13
markgor 2022-03-31 13:16:09 +08:00
非该专业,也没怎么接触过,但想问问和 SRS 对比有哪些优点吗?
之前试过用 nginx 配合模块来做直播功能 demo ,但发现 5M 带宽中 1 推 2 拉 延时有 2~3 秒。 后来换了 SRS ,同样环境 1 推 2 啦,延时基本可以忽略。 |
14
notokoy OP @lesismal
老哥一看就是高手,以后多多指教哈。 协程数量方面: 流媒体应用的特点是单个连接吞吐量大,受限于带宽,实际连接数不会太多,所以连接对应的协程数量也不会太多。 线程模型方面: 另外一点我不知道理解的对不对啊,现在的 IO 框架,还有一个优点是 IO 线程可以多个,Go 标准库底层只有一个 IO 线程。 但我觉得在流媒体应用场景下暂时也不会成为瓶颈。 所以目前以我有限的认知前提下,考量到引入 IO 框架后,依赖增加,代码风格不 Go style ,以及后续如果扩展第三方协议(比如 QUIC )库时的兼容性,我暂时不会集成 IO 框架。 如果未来我发现使用 IO 框架可以给 lalserver 带来很大的性能提升,我会考虑集成。 内存管理确实是流媒体应用的一个重点。我一直有考虑并且会持续优化。 再次感谢老哥指教,可以加个微信,有机会多讨论。 |
15
notokoy OP @markgor
优点: 相对比的话, srs 是非常非常优秀的流媒体项目, 我觉得不敢说优点,只能说最大的特点或者区别是,lal 是 Go 写的,srs 是 c++写的, 因此,可以利用 Go 的一些良好特性,比如对于用户来说,易于搭建的开发环境,跨平台能力,交叉编译,单文件运行部署等。 延时: lal 项目中有很多测试 demo ,其中就有延时测试工具。 lal 的延时和第一梯队的 c++项目是持平的。 |
16
suyuyu 2022-03-31 13:58:09 +08:00
我是第 1k 个 start
|
17
doubtlhy 2022-03-31 15:27:13 +08:00 via iPhone
之前发现一个功能差不多的开源项目 ZLMediaKit ,用了它的 webrtc 播放功能。
|
18
lesismal 2022-03-31 18:46:47 +08:00
@notokoy #34
微信太闹腾了浪费很多时间,而且歪果仁也基本不用,你有 slack 频道没?可以 slack 弄点频道、还能进军海外扩张一下。也欢迎来我的项目的频道: https://arpcnbio.slack.com/join/shared_invite/zt-16e6yu1mb-FUsil~2Jmn4Usl~IX_zv3g#/shared-invite/email 多数内部业务、尤其是需要很多功能定制的流媒体服务确实不需要太大在线量,毕竟外面有云、CDN 网络层层分担了在线数量的压力、同一 topic 只需要针对外层基础设施请求回源的节点数量就够用了,这个数量不会太大,而这些基础设施可能主流仍然是 c/cpp 的。 如果用 go 做云、CDN 之类的基础设施的开发、面向用户量很大,net.Conn 的开销也确实是还对应着挺高的硬件成本的,如果能换成 poller 和更多的内存优化,还是能声调不少资金、对能源环境问题也友好。 QUIC 这种也是路漫漫,不知道啥时候能普及,我的 poller 库只支持了 TLS/HTTP1.x/Websocket ,不打算支持 HTTP2.0 因为 2.0 太渣了。目前 go 的 QUIC 实现,也同样是一个连接至少一个协程,海量并发照样也是消耗很多资源。但是因为基于 UDP ,而 go 的 UDP 标准库已经足够简单了,所以我的 poller 库不打算支持 UDP 。至于 QUIC ,以后如果有档期,我也想搞一份不需要每个连接至少一个协程的实现,kcp-go 之类的类似标准库方案的一连接一协程也是同样的对海量并发不友好,海量并发时开销远超过其他语言的性能比较好的异步框架,剩下的只剩同步逻辑的便利,性能和消耗比其他语言异步框架劣势很多、甚至在基础设施领域,同步的便利已经无法弥补这个消耗的成本损失了。 每一样基础设施的工作量都有点大,真是想做很多但时间不够用了:joy: 你说的 go 的 io 线程数量应该是指 poller 线程数,这里先以这个作为前提,否则这样说可能不太准确。 之前我也没注意过 go 的 poller 线程数量是否只有 1 个,刚写代码试了下,net.Listen 0 个到 100 地址的时候,以及连接数 0 到 20w 的不同组合,确实都只有一个 eventfd ,而且即使不 listen 、没有连接也会有 eventfd ,是处理所有类型 fd 的 nonbloking 的: 但 top -Hp 查看,随着 net.Listen 地址数量或者连接数的增加,这 listener 和连接数的增加都对应着线程数的增加,应该是协程数多或者所需资源多时,runtime 动态增加了所需的线程。 考虑到 epoll 的不同用法和线程数量的情况,猜测 golang et 模式、epoll 线程里只是处理事件而不进行实际的读写,事件与 runtime 调度结合去触发用户协程的可读写唤醒,实际的读写则发生在用户的协程里,对应的是整个 runtime 的线程池 MPG 调度时被执行,而这个线程也是前面提到的随着负载增加而由 runtime 去增加的。所以前面我觉得“go 的 io 线程数量只有 1 个”这个说法可能不太准确。 引入 IO 框架倒还好,只是如果支持异步 IO ,编解码器要像 c/cpp 那样实现的是异步流解析,不像使用 net.Conn 同步解析这么简单,需要定制优化的地方也更多。统一实现成异步的编解码后,编解码器只收数据进行解析就可以、其实也就无所谓 IO 层是同步还是异步了,我的框架里目前就是这样,uni*是 epoll/kqueue 异步、windows 是使用 net.Conn 封装了下,框架传递数据给应用层。 并且,IO 这层节省了大量的协程数量,应用层则仍可以使用有限数量的协程池,而且应用层的协程池 1w-10w 这种 size 级别也足够用了,有特殊的阻塞需求还可以定制不同的 pool 来控制整个系统的协程资源,这样我们仍然可以在应用层写同步逻辑,我的库里现在就是这样做的,跑着效果还算挺好 |
19
notokoy OP @lesismal
老哥牛 b ,感谢纠正我的一个认知错误。 我想当然的以为 Go 的底层线程模型是一个 event loop ,并在里面完成 io 操作,然后和上层多个 worker 做交互。 刚才了解了一下,确实是 event loop 只处理事件,调用发生在上层。 不过。我依然觉得即使基于性能考虑,在流媒体场景也不是十分必须。 第一,现在一条直播流的带宽随随便便就超过 Mb 了,以后的趋势应该是越来越大。即使是 CDN 应用,受限于带宽,连接也不会太多吧。 真要是单个节点那么大的量,我觉得可以考虑多进程了。 第二,流媒体场景,还有一点和很多短连接场景不同,就是连接间有很多交互,比如数据转发。 我看过一些 IO 框架。 基本实现是支持多个 event loop ,然后连接绑定到一个固定的 event loop 中。 一些性能测试,是在自身 event loop 中 echo 数据,这种场景有两个特点: 1. 没有涉及到跨协程交互,可以无锁。 2. 理论上,一个 event loop 的所有连接可以共用一块读内存,因为不会长久持有。 这种接口型的业务场景可能对比起来比较明显。 我自己工作中也是写 c++的。以前也写过网络库。 :) slack 没有使用过。英文不行。。 |
20
lesismal 2022-03-31 23:51:50 +08:00
@notokoy #19
哥们太客气了,我也能从你的项目中学到不少流媒体的知识,交流下来,大家都有收获就很开心。 > 不过。我依然觉得即使基于性能考虑,在流媒体场景也不是十分必须。 对,所以我一直说的是海量并发场景的问题。我前面和接下来说的也是基于海量并发,普通并发量,标准库绝对足够了。 按照普通硬件配置比如 4c8c ,1-5w 这种连接数,poller 的相应性能没有优势,甚至因为异步流编解码更复杂而不如标准库响应性能好,而内存的节约也是对应交互频率等存在一定的阈值,阈值范围内,标准库更优 > 第一,现在一条直播流的带宽随随便便就超过 Mb 了,以后的趋势应该是越来越大。即使是 CDN 应用,受限于带宽,连接也不会太多吧。 > 真要是单个节点那么大的量,我觉得可以考虑多进程了。 这倒是不一定,多网卡多线,还有不同码率的包 size 未必真就是单节点不会太高连接数。而且单进程的 go 的 net.Conn 如果有瓶颈,改成单硬件上的多进程仍然高成本,只能加硬件,这个倒不是能不能搞定业务的问题,主要是硬件成本、部署节点数量维护成本之类的 > 第二,流媒体场景,还有一点和很多短连接场景不同,就是连接间有很多交互,比如数据转发。 这个确实是,但不管是 net.Conn 、poller 方案,还是 c/cpp 也都有同样的成本。所以这一点上成本的区别不大,无非就是 map 上再加点 buckets 之类的来降低锁的粒度来减少竞争成本。 所以主要的区别仍然是协程数量导致的内存、gc 、调度的压力 > 我看过一些 IO 框架。 > 基本实现是支持多个 event loop ,然后连接绑定到一个固定的 event loop 中。 对于读,姿势有点多,常见的一些用法: 1. LT 模式如果跨协程 /线程去处理 event ,则可能会有事件的多次重复派发导致事件和事件对应的读 syscall 浪费(也可以做标记避免一些重复读的浪费,但事件的重复派发也是内核和应用层的浪费),所以这种基本上是在 epoll 协程 /线程里直接进行 2. ET 模式,可以像 LT 那样直接在 epoll 循环里读,也可以把事件传给专门的 IO 协程 /线程去处理读,因为 ET 是每次有新的数据只会触发一次、不像 LT 那样只要未读完就不停触发,加上协议交互、窗口拥塞机制,所以即使跨协程 /线程处理读、ET 也不会有太多事件的重复派发,但需要注意每个 conn 的读应在确定的协程 /线程中进行,否则多个并发流读的时候可能造成数据顺序的混乱 3. ET+ONESHOT ,可以避免事件重复派发,可以让协程 /线程池中的每个任务比较均衡地获取事件进行处理,不至于像 ET 那样跨并发流传递必须注意单个 conn 的有序读;但是 ONESHOT 因为每次需要重复添加事件,如果每种使用方式实现比较良好的情况下,则 ONESHOT 方式下整体上浪费更多的 epoll syscall ,所以通常性能比 LT 或 ET 的吞吐要差一些 我自己框架中可配置 LT 或者 ET 、不支持 ET+ONESHOT ,因为实测 LT 吞吐要略好所以默认配置是 LT 。另外就是 IO 通常也不会是一个服务的全部 cpu 消耗,所以即使是丢个这几个 IO 协程 /线程,交给系统去自然调度也是能够达到整体均衡的。 对于写,也基本是 conn 直接写的多一些,缓冲区满、写失败了才会把写事件加到 event 里然后等待可写再 flush 。 > 一些性能测试,是在自身 event loop 中 echo 数据,这种场景有两个特点: > 1. 没有涉及到跨协程交互,可以无锁。 > 2. 理论上,一个 event loop 的所有连接可以共用一块读内存,因为不会长久持有。 如果是针对流媒体服务这种涉及不同连接之间的交互的,你举例子的这里的无锁有两个条件: 1. 只有一个 event loop 2. 所有 IO 、逻辑也都是在这个 event loop 中 如果不满足这两个条件,收到一个连接的数据想推送给另一个,就仍然需要锁,而满足这两个条件付出的代价可比锁大多了: 1. 只能利用单核 2 go 的指令速度还是不够快 3. 虽然是非阻塞,但涉及 IO 毕竟也是 syscall 、不是纯 cpu 消耗,所以仍然是慢 另外关于内存,其实这种场景的内存优化,普通连接数的时候,一不小心甚至比标准库更耗内存,主要区别是: 1. net.Conn 方案是读一个 message 处理一个然后下一个、同一个 buffer 可以复用 2. poller 框架节省协程数量,但是要想不被单个连接线头阻塞、IO 协程读到完整消息后需要丢给不同的协程去处理逻辑,同一个 conn 同时可能几个 message 在等待处理,而且这些 buffer 是跨协程的、生命周期更复杂,没法像标准库方案那样方便复用 结论就是各种消耗,标准方案主要与在线量相关,poller 方案主要与交互频率相关,所以前面提到阈值,不同的在线量、交互频率下,二者各有不同的优劣表现。 我最初也是写 c/cpp 的框架,写 go 的这个发现 go 比 c/cpp 的框架甚至还要复杂些,因为默认带 gc ,使用 sync.Pool 也无法精准控制,需要做更多策略。而且 go 的指令不够强,如果像很多 c/cpp 框架那样做成逻辑单线程则性能不够,所以 go 仍然需要逻辑多协程,而这又涉及到更多的锁、时序、一致性相关的细节优化,所以除了语法简单了,实现姿势其实有更多改变 PS: 最后再重复下,标准库方案绝大多数时候够用了,咱们探讨的 poller 只针对海量并发这种,所以我不是一定要劝楼主搞异步框架,够用就好:joy: |
21
lesismal 2022-04-01 00:49:06 +08:00
@notokoy
哦对了,slack 跟用英语没什么关系。我不用微信之类的是因为被一些事情恶心到了,所以跑到 slack 上躲清静。一些国人知名开源项目也在 slack 上开频道了,而且 slack 、discord 这些有现成的 github 相关的 app/功能集成,做自己开源项目还是有些便利的 |
23
tairan2006 2022-04-01 18:10:03 +08:00
lal 代码我看过,其实感觉最大的问题就是所有库都是楼主自己封装的。
建议像日志之类的模块,还是用 zap 这种成熟一些的开源库性能更好,没必要全部都自己封装… |