由于政策及版权的限制,很多内容是无法在串流平台上直播分享。
为了与朋友分享内容,自建直播平台被提上了日程。
需求
搭建自己的平台自然是能有的尽量都上,简单列举一下大概有这些:
- 可以通过 OBS/ffmpeg 推流(必须);
- 支持跨平台,包括 iOS;
- 支持对直播进行自动录像;
- 可以对观众鉴权;
- 可以对推流进行鉴权;
- 支持 SRT 推流;
- 支持 HEVC 推流/分发;
- 支持 CDN 分发;
- 支持推流协议分发;
- 延迟尽可能低。
选型
大概试用一圈开源的解决方案之后,得到了以下结论:
- 支持 rtmp -> HLS 转换即可支持 iOS 观看,由于基于 HTTP 因此同时支持 CDN 分发;
- 支持 rtmp 即可支持 OBS/ffmpeg 等直播软件的推流;
- OBS 推流 HEVC 目前不支持 SRT/rtmp,ffmpeg 推流可以用 SRT 或修改后使用 rtmp;
- HEVC 分发需要使用 DASH/HLS 等可承载 MPEG-TS 流的协议;
- 自动录像、鉴权需二次开发。
总结下来,比较常见的 nginx-rtmp 及已经被咱用了很久的 node-media-server 虽然用法已经很熟悉了,但由于对 SRT 的支持缺失以及二次开发较为吃力只能放弃了。
在很长的时间里,咱实际使的是 SRS (可能有数月之久),最初目的是实现 SRT->RTMP/HLS 的转换分发。
在此期间,发现存在两个无法绕过的 BUG:
- 启动超过一定时间(一天左右)无法接收 SRT 推流
- 主线版本不支持 HEVC 分发
于是放弃了 SRS,选择了起初看起来很乱但实际功能完善的 zlmediakit,并经过简单的二次开发最终实现全部需求。
使用
编译
zlmediakit 没有提供二进制程序,因此需要自行从源码编译。
好在编译的过程非常简单,您仅需要有 build-essential
以及 libssl-dev
即可直接编译:
mkdir build
cd build
cmake ..
make
安装
编译结束后,可以在某个路径下看到一个名为 MediaServer
的可执行文件。
同一文件夹下还可以找到 config.ini
、default.pem
以及 www
文件夹,将这三样连同可执行文件拷贝到安装目录下即可结束安装。
安装结束后的目录应有这些文件/文件夹:
www/
config.ini
default.pem
MediaServer
如有需要可以再创建一个 log
目录用于存放日志文件,具体路径在 config.ini
中配置。以及如需要使用 zlmediakit 拉流实时转码,需要在当前路径下放置一个 ffmpeg(硬链接也行)。
配置
zlmediakit 的配置在其 GitHub 的 wiki 上有较多介绍,此处仅对没有提到的部分记录:
- 如有不需要启用的直播协议,将其监听端口号设置为 0 即可;
- 启用 Cluster 模式后,推流的视频也将应用全局的无人观看等待时间,进行常规直播推流需修改
streamNoneReaderDelayMS
避免推流被关闭,此时需手动关闭转发拉流; - 对 RTMP 转发可以使用
addStreamProxy
接口,对 RTSP 流转发无法观看时需使用addFFmpegSource
接口。
其余的配置选项可以依据需要进行配置,此时除鉴权外均已经实现。
按需录像需要继续二次开发,对所有直播流进行录像已经包含在配置选项中。
二次开发
zlmediakit 的二次开发非常轻松,在配置文件中设置好 secret
以及 admin_params
后,配置对应的 hook 接口即可将对应事件托管。
例如启用推流鉴权,仅需自建一个 API Server,在配置文件中修改:
# 推流鉴权事件,置空则关闭鉴权
on_publish=http://127.0.0.1:9999/on_publish
当用户发起推流时,zlmediakit 会向上述接口发起一个 POST 请求,推流地址中的参数会包含在请求 JSON 的 params
值中。
可以借助这一值传递鉴权信息,例如推流地址为:rtmp://127.0.0.1:1935/live/test?token=114514&user=yjsnpi
params
值为 token=114514&user=yjsnpi
这一接口需要返回如下 JSON:
{
"code" : 0, # 0 表示允许推流
"msg" : "success", # 返回的消息
"enable_mp4" : False, # 是否进行录像
"enable_hls" : True, # 是否转换 HLS
"enable_rtsp": False, # 是否转换 RTSP
"enable_ts": True, # 是否转换 TS
"enable_fmp4": True, # 是否转换 HLS-LL
"enable_audio": False, # 是否转换纯音频
"add_mute_audio": True # 添加静音音频
}
咱使用的是 CherryPy 搭建 API Server,这里也可以使用您顺手的任意方式实现:
zlm_dvr_api = "http://127.0.0.1:12345/index/api/"
zlm_secret = "secret=1145141919810"
@cherrypy.expose
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
def on_publish(self):
sip = cherrypy.request.remote.ip
if sip != "127.0.0.1":
return {}
# return json
req = cherrypy.request.json
ret = {
"code" : 0,
"msg" : "success",
"enable_hls" : True,
"enable_mp4" : False,
"enable_rtsp": False,
"enable_ts": True,
"enable_fmp4": True,
"enable_audio": False,
"add_mute_audio": True
}
# split params
params = {}
for i in req.get("params", "").split("&"):
[k, v] = i.split("=", 2)
params[k] = v
# check token vaild
token = params.get("token", "")
raw = self.sess.get(f"http://token.valid.site?token={token}")
if raw.status_code != 200 and req.get("ip", "") != "127.0.0.1":
ret["code"] = -1
return ret
# check record flag
if params.get("dvr", "no") == "yes":
params["enable_mp4"] = True
api = f"{zlm_dvr_api}startRecord?type=1&vhost=__dvr__&{zlm_secret}"
api += f'&app={req.get("app", "")}&stream={req.get("stream", "")}'
sess.get(api)
return ret
如此一来即可实现推流鉴权以及设置自动录像。
观看鉴权以及观众控制均可使用类似方法进行二次开发实现。
后记
zlmediakit 依然在快速迭代中,每周都有新的提交。遇到 BUG 时不妨翻翻看 issue,说不定已经排上日程了。
zlmediakit 目前支持绝大多数的公开推流/分发协议,但并不能用于广播级的直播,对于 MPEG-TS 流的特性支持是相当缺乏的。例如 TS 中的数据流和字幕目前是完全不支持的。
对于延迟的控制,前文中一直没有提及。这是因为延迟控制是一个系统工程,需要推流端、分发端、观众端三者的共同配合。
目前基于 zlmediakit 可支持的最低延迟在 100ms 以内,需要使用 WebRTC 协议。
其他直播协议最低延迟在 3s 左右,需要使用 CBR 硬件编码 + RTMP 推流 + RTMP 低延迟参数 + 低延迟大带宽 + mpv 播放器。
以上。