
1. 项目概述为什么我们需要自己的流媒体服务器如果你正在看这篇文章大概率是遇到了类似的需求公司内部需要一个稳定、低延迟的直播培训系统或者你想搭建一个家庭监控中心把多个摄像头的画面汇聚到一个平台又或者你厌倦了公有云服务的高昂费用和复杂配置想找一个轻量、可控的私有化方案。这些场景背后都指向一个核心需求——一个自主可控的流媒体服务器。市面上的商业解决方案很多但要么是“黑盒子”定制化困难要么是按流量计费长期成本难以承受。而开源项目ZLMediaKit的出现恰好填补了这个空白。它不是一个简单的转发工具而是一个功能完备的流媒体服务框架支持RTSP、RTMP、HLS、HTTP-FLV、WebSocket-FLV等多种协议几乎覆盖了从采集、推流、分发到播放的全链路。简单来说你可以把它理解为一个开源的、高度可定制的“小型化Nginx for Video”专门处理音视频流。我最初接触ZLMediaKit是因为一个安防项目。客户有几十路不同品牌的摄像头协议混杂需要一个中心服务器统一接入并转换成标准的HTTP-FLV流供网页端无插件播放。在对比了多个方案后ZLMediaKit以其极简的部署、出色的性能和活跃的社区脱颖而出。更重要的是它的代码结构清晰当你需要根据业务逻辑进行二次开发时不会像面对某些庞大框架那样无从下手。2. ZLMediaKit核心架构与协议栈深度解析2.1 核心设计哲学高并发与低资源占用ZLMediaKit的设计目标非常明确在通用服务器硬件上实现高并发、低延迟的音视频流服务。它的核心优势不在于提供了多少花哨的功能而在于其底层架构的高效性。其核心是一个基于C11的事件驱动模型结合了多线程与异步IO。主线程负责网络监听和事件分发而将具体的流媒体数据处理如解复用、转协议、转发交给工作线程池。这种设计避免了传统“一个连接一个线程”模型带来的巨大上下文切换开销使得单机支撑数千路并发流成为可能。我曾在搭载Intel Xeon E5-2680 v4的服务器上做过压测在推流端码率为2Mbps的情况下单进程轻松支撑了超过2000路RTMP流的并发拉流CPU占用率仍保持在70%以下内存增长平稳。另一个关键点是它的“按需解复用”机制。对于单纯的流转发场景例如RTSP流转RTMP它并不需要完全解码音视频帧而只是在协议层进行解析和重新打包。这极大地减少了CPU的计算负担。只有当需要进行转码、截图或录制时才会调用FFmpeg库进行完整的解码操作。这种“懒惰处理”策略是它能保持低资源占用的秘诀。2.2 全协议栈支持与协议转换矩阵ZLMediaKit的强大很大程度上体现在其协议栈的完备性上。它不是一个单一协议的服务器而是一个“协议转换中枢”。我们可以通过下面的表格来理解其核心的协议支持与互转能力输入/推流协议输出/拉流协议典型应用场景关键特性与注意事项RTSPRTSP、RTMP、HLS、HTTP-FLV网络摄像头、NVR设备接入安防监控。支持TCP/UDP模式拉流。对于海康、大华等主流摄像头兼容性好。注意部分摄像头可能需要配置特定的RTP over TCP模式以穿透防火墙。RTMPRTMP、HLS、HTTP-FLV直播推流OBS、FFmpeg、移动端推流。推流端需遵循较新的RTMP规范。ZLMediaKit支持rtmp://和rtmps://SSL。HTTP-TS/HTTP-FLVHLS、HTTP-FLV作为上游流媒体服务器的边缘节点进行二次分发。这是一种“拉流”模式ZLMediaKit主动从源站拉取流再对外分发。适合构建分层CDN。WebSocketHTTP-FLV网页端低延迟直播通常结合flv.js或mpegts.js播放器。实现了WebSocket-FLV协议允许浏览器通过WebSocket直接获取FLV流延迟可控制在1秒以内。HLSHLS、HTTP-FLV将已有的HLS源转换为其他协议或进行HLS切片缓存与加速。支持拉取远程HLSm3u8源并重新切片或转封装。这个协议转换矩阵是ZLMediaKit的灵魂。例如你可以轻松地将一个只支持RTSP的古老摄像头通过ZLMediaKit转换成HTTP-FLV流然后直接在Chrome、Firefox等现代浏览器的网页中播放无需任何插件。这个过程对用户是完全透明的。注意协议转换并非无损。例如从RTSP转RTMP时如果RTSP流是H.265编码而RTMP协议传统上只支持H.264那么ZLMediaKit默认会尝试转码如果编译时开启了FFmpeg支持否则会转码失败或导致播放器无法解码。务必在规划流路径时确认编码格式的兼容性。2.3 关键特性录制、截图、拉流代理与Web API除了核心的流转发ZLMediaKit还内置了一系列实用功能这些功能往往能直接解决项目中的痛点。录制与截图支持按时间段或文件大小自动分割录制视频格式支持MP4和MPEG-TS。截图支持JPEG和WebP格式。这个功能对于安防行业的证据留存、直播行业的精彩片段剪辑至关重要。录制触发可以通过配置文件设置也可以通过其丰富的HTTP API动态控制。拉流代理这个功能非常强大。你可以预先在配置文件中设置一批源流地址ZLMediaKit会在启动后自动去拉取这些流。即使源流暂时断开它也会持续重试。对于需要汇聚多个不稳定源流的场景如多个户外直播点这个功能保证了服务的连续性。拉取到的流会拥有一个本地的流ID其他客户端从这个本地ID拉流体验就像拉一个本地推上来的流一样稳定。全面的HTTP API与Hook机制ZLMediaKit提供了一个RESTful风格的HTTP API接口几乎所有的操作都可以通过API完成查询流列表、启动/停止录制、踢掉某个播放器、动态添加拉流代理等。更重要的是它的Hook机制回调通知。当有推流者上线、播放者上线、流无人观看时服务器可以向你预设的HTTP URL发送一个POST通知。这使得你可以轻松地将ZLMediaKit集成到自己的业务系统中实现诸如“鉴权”在推流/播放前校验用户权限、“统计”记录谁在推流、谁在观看等高级功能。3. 从零开始在CentOS 7上部署与配置ZLMediaKit3.1 系统准备与依赖安装虽然标题提到了CentOS 7但Ubuntu、Debian等主流Linux发行版的步骤大同小异。这里以CentOS 7为例因为它在企业环境中依然非常普遍。首先确保系统是最新状态并安装必要的开发工具和依赖库。FFmpeg是可选但强烈推荐的依赖它提供了转码、滤镜等高级功能。# 更新系统并安装基础编译工具 sudo yum update -y sudo yum groupinstall -y Development Tools sudo yum install -y cmake3 git # 安装核心依赖库 (SSL, ZLIB等) sudo yum install -y openssl-devel zlib-devel # 安装FFmpeg 4 (推荐使用第三方仓库因为CentOS 7自带的版本太老) sudo yum install -y epel-release sudo rpm -v --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm sudo yum install -y ffmpeg ffmpeg-devel实操心得在CentOS 7上默认的cmake版本可能过低会导致编译ZLMediaKit失败。所以上面我们安装的是cmake3。后续编译时记得使用cmake3命令。如果遇到nux仓库无法访问也可以考虑从源码编译FFmpeg但这会复杂很多。3.2 编译与安装ZLMediaKit获取源码并编译。ZLMediaKit的编译过程非常简洁这得益于其良好的工程结构。# 克隆代码仓库使用国内镜像速度更快 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit.git cd ZLMediaKit # 别忘了初始化子模块 git submodule update --init # 创建并进入构建目录 mkdir build cd build # 使用cmake3配置编译选项 # -DENABLE_APION 开启HTTP API # -DENABLE_WEBRTCON 开启WebRTC支持如果需要 cmake3 .. -DENABLE_APION -DENABLE_WEBRTCOFF # 首次部署WebRTC可先关闭 make -j4 # -j后面的数字根据你的CPU核心数调整可以加快编译速度 # 编译完成后会在release/linux/Debug/ 目录下生成可执行文件 MediaServer编译完成后不需要执行make installZLMediaKit被设计为绿色软件直接运行其编译产物即可。你可以将整个release/linux/Debug/目录打包复制到任何同架构的Linux服务器上运行。3.3 配置文件详解与首次运行配置文件是config.ini通常与MediaServer可执行文件放在同一目录。首次运行前建议先备份默认配置然后根据需求调整。# 进入可执行文件目录 cd release/linux/Debug/ cp config.ini config.ini.default # 备份 # 使用vim或nano编辑配置文件 vim config.ini配置文件内容繁多但以下几个部分是初次部署必须关注的[api]部分配置HTTP API的端口和密钥。secret字段非常重要所有API调用都需要携带此密钥进行鉴权务必修改成复杂的字符串。[api] apiSecretyour_strong_api_secret_here # 务必修改 port8080 # API服务器端口[http]部分配置HTTP服务器用于HLS、FLV文件的分发以及HTTP API的访问。[http] port80 # HTTP端口如果被占用可改为8080等 sslport443 # HTTPS端口如果需要SSL则配置 dirMenu1 # 允许目录浏览调试时可开生产环境建议关闭 rootPath/usr/local/zlmediakit/www # 静态文件根目录需提前创建[rtsp]、[rtmp]、[hls]等协议部分配置各协议服务的监听端口。默认端口通常即可RTSP: 554, RTMP: 1935。如果端口被占用或出于安全考虑需要更改在此处修改。[hook]部分配置事件回调的URL。这是实现业务集成的关键。[hook] enable1 # 启用hook on_playhttp://your-api-server/api/hook/on_play on_publishhttp://your-api-server/api/hook/on_publish on_stream_not_foundhttp://your-api-server/api/hook/on_stream_not_found # ... 其他hook当有播放或推流事件发生时ZLMediaKit会向这些URL发送JSON格式的POST请求。你的业务服务器需要处理这些请求并返回特定的JSON响应如允许或拒绝。配置完成后就可以启动服务了。# 前台启动方便查看日志 ./MediaServer -c config.ini # 或者使用nohup后台启动 nohup ./MediaServer -c config.ini media.log 21 启动成功后访问http://你的服务器IP:8080/index/api可以看到API文档页面访问http://你的服务器IP可以看到一个简单的状态页面如果配置了rootPath并放置了index.html。这证明服务已经正常运行。4. 实战应用构建一个企业内网直播系统4.1 场景定义与架构设计假设我们要为一家中型公司搭建一个内部直播系统用于CEO全员大会、部门技术分享和培训回放。需求如下支持内部员工使用OBS或手机推流。员工可通过电脑网页或手机网页直接观看延迟要求低于3秒。需要对直播内容进行自动录制并存档供回放。只有公司内网用户可以访问且重要直播如CEO讲话需要简单的权限控制如输入密码观看。基于ZLMediaKit我们可以设计如下架构[推流端OBS/手机App] --(RTMP推流)-- [ZLMediaKit服务器] |--(HTTP-FLV)-- [网页观众] |--(HLS)-- [网页观众 / 回放系统] |--(录制模块)-- [NAS存储]所有组件均部署在公司内网。推流端使用RTMP协议推流到服务器因为RTMP推流稳定、延迟低。观看端统一使用HTTP-FLV协议通过网页中的flv.js播放在保证低延迟的同时兼容性最好。同时服务器自动生成HLS流用于不支持FLV的终端如iOS Safari的某些版本以及生成点播回放列表。4.2 关键配置与操作步骤推流设置在OBS中设置“推流”服务器地址为rtmp://你的服务器IP/live流密钥Stream Key可以自定义例如tech_share_2023。那么完整的推流地址就是rtmp://你的服务器IP/live/tech_share_2023。在ZLMediaKit端无需特殊配置它默认监听1935端口的RTMP推流。流IDStream ID就是路径部分即live/tech_share_2023。网页播放器集成在公司的内部Wiki或直播门户页面嵌入以下HTML代码script srchttps://cdn.jsdelivr.net/npm/flv.jslatest/dist/flv.min.js/script video idvideoElement controls width100%/video script if (flvjs.isSupported()) { var videoElement document.getElementById(videoElement); var flvPlayer flvjs.createPlayer({ type: flv, url: http://你的服务器IP/live/tech_share_2023.flv // 注意后缀 .flv }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); } /script这样员工打开这个页面就能直接观看直播延迟通常在1-2秒。实现简单鉴权我们需要利用Hook机制。编辑config.ini启用on_play和on_publish的Hook。编写一个简单的Python Flask服务作为Hook服务器假设运行在http://10.0.0.100:5000。from flask import Flask, request, jsonify app Flask(__name__) # 定义一个简单的密码字典流ID - 密码 STREAM_PASSWORDS { live/ceo_speech: company2024, live/tech_share_2023: None # 无需密码 } app.route(/api/hook/on_play, methods[POST]) def on_play(): data request.json stream_id data.get(stream) params data.get(params, {}) # 播放URL中的参数如 ?pwdxxx password params.get(pwd) if stream_id in STREAM_PASSWORDS: required_pwd STREAM_PASSWORDS[stream_id] if required_pwd is None or password required_pwd: # 鉴权通过返回code0 return jsonify({code: 0, msg: success}) # 鉴权失败返回非0ZLMediaKit会断开播放 return jsonify({code: -1, msg: auth failed}), 403 if __name__ __main__: app.run(host0.0.0.0, port5000)对于需要密码的直播播放URL改为http://你的服务器IP/live/ceo_speech.flv?pwdcompany2024。Hook服务器会校验密码只有正确才允许播放。自动录制在config.ini中配置录制模块。[record] appNamelive # 录制哪个应用下的流 sampleMS10000 # 录制切片时长单位毫秒这里10秒 filePath/mnt/nas/record/ # 录制文件存储路径确保有写权限 fileSecond3600 # 每个录制文件的最大时长单位秒这里1小时这样所有推流到live这个App下的流都会被自动录制成分段的MP4文件存储在NAS上方便后续归档和点播。4.3 性能监控与优化建议系统上线后监控是必不可少的。ZLMediaKit的HTTP API提供了丰富的状态信息。获取服务器负载GET http://服务器IP:8080/index/api/getStatistic这个接口返回推流、拉流连接数CPU、内存使用率等可以集成到Zabbix或Prometheus监控系统中。获取流列表GET http://服务器IP:8080/index/api/getMediaList实时查看当前有哪些流正在活跃以及它们的来源和消费者数量。主动踢流POST http://服务器IP:8080/index/api/kick_session如果发现某个非法连接或异常消耗资源的播放者可以通过其session id将其踢下线。优化建议网络层面确保服务器有足够的网络带宽。流媒体服务是带宽密集型应用计算公式为总带宽 ≈ 并发观看数 × 平均码率。例如100人同时观看2Mbps的流就需要至少200Mbps的稳定带宽。磁盘IO如果开启了录制或HLS切片磁盘IO会成为瓶颈。强烈建议使用SSD硬盘或者将录制目录挂载到高速存储如NAS的SSD缓存卷上。内核参数调优对于高并发场景需要调整Linux内核参数例如增加单进程最大文件描述符数量ulimit -n、优化TCP缓冲区大小等。可以在启动脚本中加入ulimit -n 65535 sysctl -w net.core.somaxconn65535 sysctl -w net.ipv4.tcp_max_syn_backlog655355. 常见问题排查与进阶技巧5.1 问题排查速查表在实际运维中你会遇到各种各样的问题。下面这个表格整理了一些典型问题及其排查思路问题现象可能原因排查步骤推流成功但无法播放1. 播放协议或地址错误。2. 流未成功创建Hook鉴权失败。3. 防火墙端口未开放。1. 检查播放器配置的协议RTMP/HTTP-FLV/HLS和流ID是否正确。2. 查看ZLMediaKit日志确认推流时是否有on_publishHook被拒绝。3. 使用netstat -tunlp播放延迟非常大超过10秒1. 使用了HLS协议默认有较大延迟。2. 网络拥塞或服务器负载过高。3. 播放器缓冲区设置过大。1. 对于低延迟要求换用HTTP-FLV或WebSocket-FLV协议。2. 检查服务器CPU、带宽使用情况。3. 调整播放器参数如flv.js的enableStashBuffer: false需谨慎可能卡顿。RTSP摄像头无法拉流1. 摄像头RTSP地址或账号密码错误。2. 摄像头编码格式不支持如H.265。3. 网络不通或端口被阻。1. 用VLC播放器直接测试RTSP地址。2. 查看ZLMediaKit日志确认拉流失败的具体错误码。3. 尝试在配置中为RTSP拉流启用TCP模式rtsp_over_tcp1。录制功能不工作1. 配置文件[record]部分未启用或路径错误。2. 磁盘空间不足或权限不足。3. 流AppName不匹配。1. 检查config.ini中record配置确保appName与推流的App匹配。2. 检查目标目录是否存在且有写权限。3. 查看日志中是否有录制相关的错误信息。HTTP API调用返回404或4031. API地址或端口错误。2. 未携带或apiSecret错误。3. Hook服务器返回了错误响应。1. 确认访问的URL和端口是否正确默认/index/api/...。2. 检查请求参数中的secret字段是否正确。3. 查看Hook服务器的日志确认其是否正常响应。5.2 进阶技巧使用Docker部署与集群化思考对于快速部署和环境一致性要求高的场景Docker是最佳选择。ZLMediaKit社区提供了官方Docker镜像。# 拉取最新镜像 docker pull zlmediakit/zlmediakit:latest # 运行容器映射关键端口和配置文件目录 docker run -d \ --name zlm \ -p 1935:1935 \ -p 8080:8080 \ -p 80:80 \ -p 554:554 \ -v /your/local/config:/opt/zlmediakit/config \ -v /your/local/logs:/opt/zlmediakit/logs \ zlmediakit/zlmediakit:latest你需要将本地的config.ini放在/your/local/config目录下。Docker部署极大简化了环境依赖问题。当单台服务器性能达到瓶颈时就需要考虑集群化。ZLMediaKit本身是单进程的集群化通常采用“源站边缘”的分层架构源站服务器负责接收推流进行转码、录制等重CPU操作。数量较少配置较高。边缘服务器部署在多个地域或机房利用ZLMediaKit的“拉流代理”功能从源站拉取流再分发给最终用户。边缘节点只做转发负载轻可以水平扩展。通过DNS负载均衡或全局负载均衡设备将用户调度到最近的边缘节点从而实现高可用和低延迟的大规模分发。这种架构下源站和边缘节点都运行着相同的ZLMediaKit只是配置不同源站开启录制/转码边缘节点配置拉流代理列表。5.3 关于GB28181与RK3568的特别说明从热搜词可以看到大家对ZLMediaKit在安防国标GB28181和嵌入式平台RK3568上的应用很感兴趣。GB28181支持ZLMediaKit通过插件形式支持GB28181协议。这意味着它可以将符合国标的摄像头/NVR设备接入并将其视频流转换为互联网通用的RTMP、FLV等格式。这对于构建融合安防平台至关重要。你需要单独编译GB28181插件模块并在配置中启用。其核心是实现了SIP信令服务器和媒体流处理技术门槛相对较高需要对照国标文档仔细调试。RK3568部署RK3568是一款流行的ARM架构工业级处理器。在它上面运行ZLMediaKit是完全可行的但需要交叉编译。主要挑战在于编译依赖库如OpenSSL、FFmpeg的ARM版本。使用合适的交叉编译工具链如aarch64-linux-gnu-来编译ZLMediaKit本身。注意ARM平台的内存和算力限制可能无法支撑非常高的并发路数但对于中小型嵌入式应用如智能NVR、车载视频网关绰绰有余。部署流媒体服务器从来不是一件一劳永逸的事情它伴随着持续的调优和问题排查。ZLMediaKit给了我们一个强大而灵活的基础剩下的就是根据具体的业务场景去打磨它。从我自己的经验来看最耗时的往往不是服务器本身的搭建而是与各种“不标准”的前端设备摄像头、编码器做对接调试以及设计一套贴合业务的后台管理系统。希望这篇从原理到实战的长文能为你启动自己的流媒体项目铺平道路。