You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用.ts分片创建HLS主播放列表?Nginx+FFmpeg相关技术问询

HLS主播放列表构建:可行性与即时生成方案

嘿,我来帮你理清这个HLS主播放列表的问题~

一、直接用不同播放列表的.ts分片创建主播放列表?不可行!

首先得明确HLS的核心规范:主播放列表(Master Playlist)的设计目的是指向变体播放列表(Variant Playlist)——也就是那些包含对应码率/分辨率.ts分片的.m3u8文件,而不是直接引用单个.ts分片。

这是因为HLS客户端的逻辑是:先拉取主列表,再根据当前带宽选择合适的变体列表,最后从变体列表里逐个拉取.ts分片播放。如果主列表直接放.ts文件,完全不符合RFC 8216的标签规则,客户端根本无法识别这种结构,直接会播放失败。

举个正确的主列表结构例子:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=500000,RESOLUTION=640x360
360p.m3u8

你要是把里面的.m3u8换成.ts,播放器直接会报错。

二、如何在Nginx收到首个.ts分片时立即生成主播放列表?

虽然直接用.ts分片做主列表不行,但我们有几种办法能实现“首个分片出现就生成主列表”的需求,下面是具体方案:

1. 用Shell脚本+文件监听自动生成

Nginx的ngx_http_hls_module在收到FFmpeg推流的第一个.ts分片时,会自动生成对应的变体播放列表(比如720p.m3u8)。我们可以用inotifywait监听变体列表的创建事件,一旦检测到第一个变体列表出现,就自动生成主列表:

#!/bin/bash
# 监听的HLS目录,改成你的Nginx web目录下的HLS路径
MONITOR_DIR="/usr/share/nginx/html/hls"
MASTER_PLAYLIST="$MONITOR_DIR/master.m3u8"

# 等待第一个变体列表创建
inotifywait -e create "$MONITOR_DIR" | while read -r dir events filename; do
    # 只处理变体m3u8,跳过主列表本身
    if [[ $filename == *.m3u8 && $filename != "master.m3u8" ]]; then
        # 初始化主列表
        echo "#EXTM3U" > "$MASTER_PLAYLIST"
        # 遍历所有变体列表,添加到主列表
        for variant in "$MONITOR_DIR"/*.m3u8; do
            if [[ $(basename "$variant") != "master.m3u8" ]]; then
                # 从文件名提取分辨率(比如720p.m3u8),计算带宽
                res=$(echo "$filename" | grep -oP '\d+p' | sed 's/p//')
                bandwidth=$((res * 1000)) # 转换成bps单位
                resolution="${res}x$((res*9/16))" # 16:9比例计算高度
                echo "#EXT-X-STREAM-INF:BANDWIDTH=$bandwidth,RESOLUTION=$resolution" >> "$MASTER_PLAYLIST"
                echo "$(basename "$variant")" >> "$MASTER_PLAYLIST"
            fi
        done
        # 生成一次就退出,或者去掉break持续监听更新
        break
    fi
done

把这个脚本做成系统服务(比如systemd),或者在Nginx启动时自动运行,就能实现首个分片出现时自动生成主列表。

2. 用Nginx Lua模块动态生成主列表

如果你的Nginx安装了ngx_http_lua_module,可以写个Lua脚本,在客户端请求master.m3u8时实时扫描变体列表并生成主列表内容——这样不需要提前生成文件,只要有至少一个变体列表存在(也就是已经有.ts分片了),就能返回有效的主列表:

首先创建Lua脚本/etc/nginx/lua/gen_master.lua:

local hls_dir = "/usr/share/nginx/html/hls"
local master_content = "#EXTM3U\n"

-- 遍历目录下的变体m3u8文件
local handle = io.popen("ls -1 " .. hls_dir .. "/*.m3u8 | grep -v master.m3u8")
for filename in handle:lines() do
    local base_name = string.match(filename, "/([^/]+)$")
    -- 从文件名提取分辨率(比如720p.m3u8)
    local res = string.match(base_name, "(%d+)p")
    if res then
        local bandwidth = tonumber(res) * 1000 -- 假设720p对应720000 bps
        local resolution = res .. "x" .. math.floor(tonumber(res)*9/16)
        master_content = master_content .. "#EXT-X-STREAM-INF:BANDWIDTH=" .. bandwidth .. ",RESOLUTION=" .. resolution .. "\n"
        master_content = master_content .. base_name .. "\n"
    end
end
handle:close()

-- 设置响应头并返回主列表内容
ngx.header.content_type = "application/vnd.apple.mpegurl"
ngx.print(master_content)

然后在Nginx配置里添加这个location:

location /master.m3u8 {
    content_by_lua_file /etc/nginx/lua/gen_master.lua;
}

这样客户端请求master.m3u8时,只要有一个变体列表存在(即首个.ts分片已生成),就会动态生成并返回主列表,非常灵活。

3. FFmpeg推流时同步生成主列表

如果你的推流场景是固定码率的变体流,可以在FFmpeg推流命令执行后,延迟几秒(等首个分片生成)直接生成主列表:

# 推720p流到后台
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls -hls_list_size 0 http://your-nginx/hls/720p.m3u8 &
# 推360p流到后台
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -vf scale=640:360 -f hls -hls_list_size 0 http://your-nginx/hls/360p.m3u8 &
# 等待1秒让首个分片生成,然后写入主列表
sleep 1 && echo -e "#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=720000,RESOLUTION=1280x720\n720p.m3u8\n#EXT-X-STREAM-INF:BANDWIDTH=360000,RESOLUTION=640x360\n360p.m3u8" > /usr/share/nginx/html/hls/master.m3u8

这种方法最简单,但需要预先知道所有变体流的参数,适合固定场景。


内容的提问来源于stack exchange,提问作者Pallav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:52:40