实战:构建基于 Docker-Compose 的HLS (m3u8) 实时转 FLV,基于 ZLMediaKit 的低延迟方案

在流媒体开发中,如何让系统既能实现 HLS 转 FLV 的低延迟播放,又能做到 无人观看时自动清理资源?本文将带你通过 Docker-Compose 快速搭建这套系统,并深入解析其背后的自动化逻辑。


1. 宿主机环境准备

在启动容器前,我们需要在宿主机上建立持久化目录,以实现配置和数据的“动静分离”。

# 创建工作目录
mkdir -p /opt/zlm/conf /opt/zlm/www

# 修改 www 目录权限,确保容器有权写入和清理视频切片
chmod -R 777 /opt/zlm/www

2. 定制精简版配置文件 (config.ini)

/opt/zlm/conf/ 目录下创建 config.ini。重点优化了资源回收机制:

[api]
# 你的 API 访问密钥,调用 addStreamProxy 时必须携带
secret=WkkrdtvvniujOp1ITUpvYOdE16eUOctF
# 截图保存路径(如不需要可保持默认)
downloadRoot=./www

[ffmpeg]
# FFmpeg 程序路径,用于转码
bin=/usr/bin/ffmpeg
# 转码命令模板,默认转为 H.264 + AAC
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s

[protocol]
# 【核心配置】无人观看时,直接关闭流,释放内存和连接
auto_close=1
# 是否开启 HLS 转换。开启后会在 www 目录生成 ts 切片
enable_hls=1
# 是否开启 RTMP/FLV 转换
enable_rtmp=1
# HLS 协议是否按需生成。设置为 1 后,没人看就不生成 HLS 切片,节省磁盘 IO
hls_demand=1
# RTMP/FLV 是否按需生成。设置为 1 后,没人看就不占用转码资源
rtmp_demand=1

[general]
# 【核心配置】无人观看触发自动关闭的延迟时间(毫秒)
# 设置为 10000 代表最后一个播放器关掉 10 秒后,彻底清理该流
streamNoneReaderDelayMS=10000
# 绑定的本地网卡 IP
listen_ip=::
# 服务器唯一 ID
mediaServerId=your_server_id

[hls]
# HLS 切片大小(秒)
segDur=2
# m3u8 中保留的切片个数
segNum=3
# 切片从 m3u8 移除后,在磁盘上额外保留的个数。配合自动清理,建议设小
segRetain=2
# 直播 HLS 文件彻底删除的延迟(秒)
deleteDelaySec=10

[http]
# http服务器字符编码集
charSet=utf-8
# http链接超时时间
keepAliveSecond=30
# http请求体最大字节数,如果post的body太大,则不适合缓存body在内存
maxReqSize=40960
#http文件服务器读文件缓存大小,单位BYTE,调整该参数可以优化文件io性能
sendBufSize=65536

# HTTP 端口(对应容器内的 80 端口)
port=80
#https服务器监听端口
sslport=443
# 允许跨域,方便前端 flv.js 播放
allow_cross_domains=1
#允许访问http api和http文件索引的ip地址范围白名单,置空情况下不做限制
allow_ip_range=::1,127.0.0.1,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255

[rtmp]
# RTMP 端口(对应容器内的 1935 端口)
port=1935

[rtsp]
# RTSP 端口(对应容器内的 554 端口)
port=554

3. 使用 Docker-Compose 编排服务

创建 /opt/zlm/docker-compose.yml,将端口映射与目录挂载结构化:

version: '3'
services:
  zlm:
    image: zlmediakit/zlmediakit:master
    container_name: zlm_server
    restart: always
    ports:
      - "10080:80"     # HTTP API & FLV 播放端口
      - "11935:1935"   # RTMP 端口
      - "10554:554"    # RTSP 端口
      - "10000:10000"  # RTP 代理端口
      - "10000:10000/udp"
      - "8000:8000/udp" # WebRTC 端口
      - "9000:9000/udp" # SRT 端口
    volumes:
      # 挂载自定义配置文件
      - ./conf/config.ini:/opt/media/conf/config.ini
      # 挂载静态资源及视频切片目录
      - ./www:/opt/media/www

启动命令: docker-compose up -d


4. 验证效果

4.1 获取flv的url

参考MediaServer支持的HTTP API文档

${host}:zlmediakit的地址,例如:127.0.0.1:10080
${secret}:配置文件config.ini中的秘钥
${streamId}:流的id名(唯一)
${url}:拉流地址,例如http://127.0.0.1:83/ddfds/live.m3u8

通过请求 http://${host}/index/api/addStreamProxy?secret=${secret}&vhost=__defaultVhost__&app=live&stream=${streamId}&url=${url}&auto_close=1
响应返回是如下则说明转码成功:

{
  "code": 0,
  "data": {
    "key": "__defaultVhost__/live/api_nm2sdt1"
  }
}

获取到最终的url:http://${host}/live/${streamId}.live.flv

4.2 验证FLV

通过http://jessibuca.monibuca.com/player-pro.html快速验证。
在这里插入图片描述

5. 后端自动化:WebHook 与流管理

为了不让前端暴露 secret,我们需要一个后端(如 Java SpringBoot)作为中控。

5.1 自动拉流接口

前端请求后端接口,后端负责调用 ZLM 并返回 FLV 地址:

@GetMapping("/getPlayUrl")
public String getPlayUrl(@RequestParam String originUrl, @RequestParam String streamId) {
    // 1. 调用 ZLM addStreamProxy 接口
    String zlmApi = "http://127.0.0.1:10080/index/api/addStreamProxy";
    // 参数包含 secret, app, stream, url 等
    // 2. 成功后拼接并返回 FLV 地址
    return "http://127.0.0.1:10080/live/" + streamId + ".live.flv";
}

5.2 WebHook 实时监控

config.ini[hook] 部分配置回调地址。当流状态变化时,ZLM 会主动告诉后端。

  • on_stream_changed: 监控流是否注册成功或注销。
  • on_stream_none_reader: 当流因为无人观看被销毁时,后端可以同步更新数据库状态。

6. 总结与避坑

  1. 挂载细节:只挂载 confwww。不要挂载整个 /opt/media,否则容器内的二进制程序可能会被宿主机的空文件夹覆盖导致启动失败。
  2. 清理机制auto_close=1 配合 streamNoneReaderDelayMS 是节省资源的黄金组合。它能确保在监控路数较多时,服务器不会因为过载而崩溃。
  3. 安全提醒:API 秘钥(Secret)应仅在受信任的后端环境中使用。

通过这套方案,你不仅拥有了一个流媒体服务器,更建立了一个可以自我调度、自动清理的智能监控平台。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐