lolly/docs/nginx/36-nginx-streaming-media.md
xfy 972eab4267 refactor(docs): 重构文档目录结构,nginx 文档移至子目录
将 docs/ 根目录下的 nginx 相关文档统一移动到 docs/nginx/ 子目录,
提高文档组织性和可维护性。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 10:48:14 +08:00

19 KiB
Raw Blame History

NGINX 流媒体模块指南

1. 模块概述

NGINX 提供多个模块支持 HTTP 流媒体服务,涵盖直播、点播和伪流媒体场景。

1.1 模块对比

模块 协议/格式 用途 可用性
ngx_http_hls_module HLS (HTTP Live Streaming) MP4/MOV 文件的 HLS 直播流 NGINX Plus 商业版
ngx_http_flv_module FLV (Flash Video) FLV 文件伪流媒体 开源版
ngx_http_mp4_module MP4 (H.264/AAC) MP4 文件伪流媒体 开源版
ngx_http_f4f_module F4F/F4M (Adobe HDS) Adobe HTTP Dynamic Streaming NGINX Plus 商业版

1.2 伪流媒体 vs 直播流

伪流媒体 (Pseudo-Streaming)

  • 客户端通过 start 参数请求特定时间点
  • 服务器从该时间点开始发送视频数据
  • 适用于点播场景,支持随机 seek

直播流 (Live Streaming)

  • 实时生成媒体片段 (TS/F4F)
  • 动态更新播放列表 (M3U8/F4M)
  • 支持 HLS、HDS 等自适应码率协议

2. HLS 模块 (ngx_http_hls_module)

为 MP4 和 MOV 媒体文件提供 HTTP Live Streaming (HLS) 服务器端支持。

2.1 编译配置

# 商业订阅版本已包含此模块
# 无需额外编译参数

2.2 指令详解

hls

语法hls;

默认值:无

上下文location

说明:在 surrounding location 中开启 HLS 流媒体服务。

location / {
    hls;
}

hls_fragment

语法hls_fragment time;

默认值hls_fragment 5s;

上下文http, server, location

说明:为未带 len 参数请求的播放列表 URI 定义默认片段长度。

hls_fragment 10s;    # 每个 TS 片段 10 秒

hls_buffers

语法hls_buffers number size;

默认值hls_buffers 8 2m;

上下文http, server, location

说明:设置用于读写数据帧的最大缓冲区数量和大小。

hls_buffers 10 10m;  # 10 个缓冲区,每个 10MB

hls_forward_args

语法hls_forward_args on | off;

默认值hls_forward_args off;

上下文http, server, location

说明:将播放列表请求中的参数添加到片段 (fragment) 的 URI 中。

用途

  • 客户端授权
  • 配合 ngx_http_secure_link_module 保护 HLS 流
hls_forward_args on;

hls_mp4_buffer_size

语法hls_mp4_buffer_size size;

默认值hls_mp4_buffer_size 512k;

上下文http, server, location

说明:设置用于处理 MP4 和 MOV 文件的初始缓冲区大小。

hls_mp4_buffer_size 1m;

hls_mp4_max_buffer_size

语法hls_mp4_max_buffer_size size;

默认值hls_mp4_max_buffer_size 10m;

上下文http, server, location

说明:在元数据处理期间,缓冲区最大不能超过此值,否则返回 500 错误。

错误日志"mp4 moov atom is too large"

hls_mp4_max_buffer_size 5m;

2.3 请求参数

HLS 播放列表支持以下 URI 参数:

参数 说明 示例
start 起始时间(秒) ?start=1.000
end 结束时间(秒) ?end=2.200
offset 偏移时间(秒) ?offset=1.000
len 片段长度(秒) ?len=10

2.4 配置示例

基本 HLS 配置

server {
    listen 80;
    server_name hls.example.com;

    location / {
        hls;
        hls_fragment            5s;
        hls_buffers             10 10m;
        hls_mp4_buffer_size     1m;
        hls_mp4_max_buffer_size 5m;
        root /var/video/;
    }
}

安全链接配置

配合 hls_forward_args on 使用 secure_link

http {
    # 提取基础 URI去掉 .m3u8 和 .ts 后缀)
    map $uri $hls_uri {
        ~^(?<base_uri>.*)\.m3u8$ $base_uri;
        ~^(?<base_uri>.*)\.ts$   $base_uri;
        default                 $uri;
    }

    server {
        listen 80;
        server_name secure-hls.example.com;

        location /hls/ {
            hls;
            hls_forward_args on;
            alias /var/videos/;

            # 安全链接验证
            secure_link $arg_md5,$arg_expires;
            secure_link_md5 "$secure_link_expires$hls_uri$remote_addr secret";

            if ($secure_link = "") { return 403; }
            if ($secure_link = "0") { return 410; }
        }
    }
}

2.5 请求 URI 示例

对于文件 /var/video/test.mp4

类型 URI 示例
播放列表 http://hls.example.com/test.mp4.m3u8?offset=1.000&start=1.000&end=2.200
片段 http://hls.example.com/test.mp4.ts?start=1.000&end=2.200

3. FLV 模块 (ngx_http_flv_module)

为 Flash Video (FLV) 文件提供伪流媒体服务器端支持。

3.1 编译配置

# 此模块默认不构建,需要显式启用
./configure --with-http_flv_module ...

3.2 指令详解

flv

语法flv;

默认值:无

上下文location

说明:在 surrounding location 中开启 FLV 模块处理。

行为

  • 特殊处理包含 start 参数的请求
  • 从请求的字节偏移量发送文件内容
  • 自动前置 FLV 头
location ~ \.flv$ {
    flv;
}

3.3 请求参数

参数 说明 示例
start 起始字节偏移量 ?start=1000

3.4 配置示例

server {
    listen 80;
    server_name video.example.com;

    location /videos/ {
        root /var/www/;
    }

    location ~ \.flv$ {
        flv;
        root /var/www/videos/;
    }
}

4. MP4 模块 (ngx_http_mp4_module)

为 MP4 文件提供服务器端伪流媒体支持,允许通过 startend 参数进行随机 seek。

4.1 编译配置

# 此模块默认不构建,需要显式启用
./configure --with-http_mp4_module ...

4.2 指令详解

mp4

语法mp4;

默认值:无

上下文location

说明:在 surrounding location 中开启 MP4 模块处理。

location /video/ {
    mp4;
}

mp4_buffer_size

语法mp4_buffer_size size;

默认值mp4_buffer_size 512K;

上下文http, server, location

说明:设置用于处理 MP4 文件的初始缓冲区大小。

mp4_buffer_size 1m;

mp4_max_buffer_size

语法mp4_max_buffer_size size;

默认值mp4_max_buffer_size 10M;

上下文http, server, location

说明:元数据处理期间缓冲区的最大大小。若 moov atom 过大,返回 500 错误。

mp4_max_buffer_size 5m;

mp4_limit_rate

语法mp4_limit_rate on | off | factor;

默认值mp4_limit_rate off;

上下文http, server, location

说明:基于文件平均比特率限制响应传输速率。

参数说明

  • on:限速因子为 1.1
  • factor:自定义限速因子

注意:此指令仅适用于商业订阅版本。

mp4_limit_rate on;       # 因子 1.1
mp4_limit_rate 1.5;      # 自定义因子 1.5

mp4_limit_rate_after

语法mp4_limit_rate_after time;

默认值mp4_limit_rate_after 60s;

上下文http, server, location

说明:设置开始限速前的初始媒体数据播放时长。

注意:此指令仅适用于商业订阅版本。

mp4_limit_rate_after 30s;

mp4_start_key_frame

语法mp4_start_key_frame on | off;

默认值mp4_start_key_frame off;

上下文http, server, location

说明:强制输出视频从关键帧开始。

行为

  • start 指定的位置非关键帧,使用 edit list 隐藏初始帧
  • 需要 NGINX 1.21.4+
  • 主流播放器Chrome、Safari 等)支持 edit list
mp4_start_key_frame on;

4.3 请求参数

参数 说明 示例
start 起始时间(秒) ?start=238.88
end 结束时间(秒) ?end=555.55

组合示例?start=238.88&end=555.55

4.4 配置示例

基本 MP4 配置

server {
    listen 80;
    server_name video.example.com;

    location /video/ {
        mp4;
        mp4_buffer_size       1m;
        mp4_max_buffer_size   5m;
        root /var/videos/;
    }
}

高级配置(商业版功能)

server {
    listen 80;
    server_name video.example.com;

    location /video/ {
        mp4;
        mp4_buffer_size       1m;
        mp4_max_buffer_size   5m;
        mp4_limit_rate        on;
        mp4_limit_rate_after  30s;
        mp4_start_key_frame   on;
        root /var/videos/;
    }
}

4.5 性能优化建议

文件结构优化

  • 若 moov atom元数据位于文件末尾NGINX 需要读取整个文件
  • 建议使用工具(如 qt-faststart)将 moov atom 移到文件开头:
# 使用 qt-faststart 优化 MP4 文件
qt-faststart input.mp4 output.mp4

5. F4F 模块 (ngx_http_f4f_module)

提供 Adobe HTTP Dynamic Streaming (HDS) 的服务器端支持。

5.1 模块说明

功能

  • 处理 /videoSeg1-Frag1 形式的请求
  • 使用 .f4x 索引文件从 .f4f 文件中提取片段

可用性:仅作为 NGINX Plus 商业订阅的一部分提供。

5.2 指令详解

f4f

语法f4f;

默认值:无

上下文location

说明:在 surrounding location 中开启 F4F 模块处理。

location /video/ {
    f4f;
}

f4f_buffer_size

语法f4f_buffer_size size;

默认值f4f_buffer_size 512k;

上下文http, server, location

说明:设置用于读取 .f4x 索引文件的缓冲区大小。

f4f_buffer_size 1m;

5.3 配置示例

server {
    listen 80;
    server_name hds.example.com;

    location /video/ {
        f4f;
        f4f_buffer_size 1m;
        root /var/f4f/;
    }
}

6. 完整配置示例

6.1 综合流媒体服务器

# 负载均衡后端(用于回源)
upstream media_backend {
    server 192.168.1.10:8080 weight=5;
    server 192.168.1.11:8080 weight=5;
    server 192.168.1.12:8080 backup;
}

# 限速配置
limit_rate_after 1m;
limit_rate 1m;

server {
    listen 80;
    server_name media.example.com;

    # 日志格式
    log_format media '$remote_addr - $remote_user [$time_local] '
                     '"$request" $status $bytes_sent '
                     '"$http_referer" "$http_user_agent" '
                     'time=$request_time';

    access_log /var/log/nginx/media-access.log media;

    # HLS 流媒体NGINX Plus
    location /hls/ {
        hls;
        hls_fragment            5s;
        hls_buffers             10 10m;
        hls_mp4_buffer_size     1m;
        hls_mp4_max_buffer_size 5m;
        alias /var/videos/hls/;

        # 可选:安全链接
        # hls_forward_args on;
        # secure_link ...
    }

    # FLV 伪流媒体
    location /flv/ {
        location ~ \.flv$ {
            flv;
            alias /var/videos/flv/;
        }
    }

    # MP4 伪流媒体
    location /mp4/ {
        location ~ \.mp4$ {
            mp4;
            mp4_buffer_size       1m;
            mp4_max_buffer_size   5m;
            alias /var/videos/mp4/;

            # NGINX Plus 功能
            # mp4_limit_rate        on;
            # mp4_limit_rate_after  30s;
            # mp4_start_key_frame   on;
        }
    }

    # F4F 流媒体NGINX Plus
    location /hds/ {
        f4f;
        f4f_buffer_size 1m;
        alias /var/videos/hds/;
    }

    # 视频文件通用缓存配置
    location ~* \.(mp4|flv|f4f|ts|m3u8)$ {
        expires 1d;
        add_header Cache-Control "public, immutable";

        # 跨域支持
        add_header Access-Control-Allow-Origin "*";
        add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";
    }

    # 播放列表不缓存(实时更新)
    location ~ \.m3u8$ {
        expires -1;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
    }

    # 状态监控
    location /nginx_status {
        stub_status on;
        allow 127.0.0.1;
        allow 10.0.0.0/8;
        deny all;
    }
}

# HTTPS 配置
server {
    listen 443 ssl http2;
    server_name media.example.com;

    ssl_certificate     /etc/ssl/certs/media.crt;
    ssl_certificate_key /etc/ssl/private/media.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    # 复用 HTTP 配置
    include /etc/nginx/conf.d/media-locations.conf;
}

6.2 带转码的流媒体配置

# 使用 ngx_rtmp_module第三方模块做 RTMP 转 HLS
rtmp {
    server {
        listen 1935;

        application live {
            live on;

            # 转 HLS
            hls on;
            hls_path /var/videos/hls/;
            hls_fragment 5s;
            hls_playlist_length 60s;

            # 多码率
            hls_variant _low BANDWIDTH=500000;
            hls_variant _mid BANDWIDTH=1500000;
            hls_variant _high BANDWIDTH=5000000;
        }
    }
}

http {
    server {
        listen 80;
        server_name live.example.com;

        # 服务 HLS 流
        location /hls/ {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }

            alias /var/videos/hls/;
            add_header Cache-Control "no-cache";
            add_header Access-Control-Allow-Origin "*";
        }
    }
}

7. 与 Lolly 项目的关系和建议

7.1 当前状态对比

特性 NGINX 流媒体模块 Lolly 当前状态
HLS 支持 完整(商业版) 暂未实现
FLV 支持 完整(开源版) 暂未实现
MP4 点播 完整(需编译) 暂未实现
F4F/HDS 完整(商业版) 暂未实现
静态文件 完整 支持
文件缓存 完整 支持

7.2 实现建议

对于 Lolly 项目,可以考虑以下实现策略:

1. 伪流媒体实现MP4/FLV

// handler/streaming.go
package handler

import (
    "github.com/valyala/fasthttp"
)

// MP4Handler 处理 MP4 伪流媒体请求
func MP4Handler(ctx *fasthttp.RequestCtx) {
    start := ctx.QueryArgs().GetFloat64("start")
    end := ctx.QueryArgs().GetFloat64("end")

    // 解析 MP4 moov atom计算偏移量
    // 从指定时间点开始传输
    // 处理 end 参数截断
}

// FLVHandler 处理 FLV 伪流媒体请求
func FLVHandler(ctx *fasthttp.RequestCtx) {
    start := ctx.QueryArgs().GetInt("start")

    // 发送 FLV 头
    // 从指定字节偏移开始传输
}

2. HLS 服务实现

// handler/hls.go
package handler

import (
    "fmt"
    "os"
    "path/filepath"
    "strings"
)

// HLSPlaylistHandler 生成 M3U8 播放列表
func HLSPlaylistHandler(ctx *fasthttp.RequestCtx) {
    videoPath := getVideoPath(ctx)

    // 解析 MP4计算片段
    segments := generateSegments(videoPath, fragmentDuration)

    // 生成 M3U8 内容
    playlist := generateM3U8(segments)

    ctx.SetContentType("application/vnd.apple.mpegurl")
    ctx.WriteString(playlist)
}

// HLSSegmentHandler 返回 TS 片段
func HLSSegmentHandler(ctx *fasthttp.RequestCtx) {
    // 从 MP4 提取指定时间范围的 TS 数据
    // 或使用预生成的 TS 文件
}

3. 配置扩展示例

# lolly.yaml 流媒体配置示例
server:
  # 静态文件服务(已支持)
  static:
    - path: "/"
      root: "/var/www/html"

  # 流媒体服务(建议新增)
  streaming:
    # HLS 配置
    hls:
      enabled: true
      path: "/hls/"
      root: "/var/videos/"
      fragment: "5s"
      buffers: 10
      buffer_size: "10m"
      mp4_buffer_size: "1m"
      mp4_max_buffer_size: "5m"

    # MP4 伪流媒体
    mp4:
      enabled: true
      path: "/mp4/"
      root: "/var/videos/"
      buffer_size: "1m"
      max_buffer_size: "5m"

    # FLV 伪流媒体
    flv:
      enabled: true
      path: "/flv/"
      root: "/var/videos/"

    # 跨域配置
    cors:
      enabled: true
      origins: ["*"]
      methods: ["GET", "HEAD", "OPTIONS"]

7.3 技术实现要点

MP4 文件处理

// 关键:解析 moov atom计算时间到字节的映射
type MP4Parser struct {
    Moov *MoovBox
}

type MoovBox struct {
    Tracks []*Track
}

type Track struct {
    Timescale uint32
    Samples   []*Sample
}

// SeekToTime 返回指定时间对应的文件偏移和样本索引
func (p *MP4Parser) SeekToTime(seconds float64) (offset int64, sampleIdx int) {
    // 遍历样本表,找到对应时间点的样本
    // 计算文件偏移
}

HLS 切片生成

// SegmentInfo 表示一个 TS 片段
type SegmentInfo struct {
    Sequence  int
    Duration  float64
    StartTime float64
    EndTime   float64
}

// GenerateSegments 将 MP4 切分为片段信息
func GenerateSegments(videoPath string, fragmentDuration float64) ([]SegmentInfo, error) {
    // 解析 MP4 结构
    // 按关键帧边界分割片段
    // 返回片段信息列表
}

7.4 依赖库建议

功能 推荐库
MP4 解析 github.com/abema/go-mp4github.com/deepch/mp4
HLS 生成 自行实现或使用 github.com/grafov/m3u8
视频转码 github.com/xfrr/goffmpeg (FFmpeg 绑定)
FLV 解析 github.com/yapingcat/gomedia

7.5 性能优化建议

  1. 文件缓存:复用现有文件缓存系统缓存解析后的 MP4 元数据
  2. 预生成切片:对于点播内容,预先生成 TS 片段文件
  3. 零拷贝传输:大视频文件使用 sendfile
  4. Goroutine 池:控制并发转码任务数量
  5. 内存复用:使用 sync.Pool 复用缓冲区

7.6 安全建议

  1. 路径遍历防护:验证请求路径,防止访问非视频目录
  2. 限速控制:对视频流进行带宽限制
  3. 防盗链:使用 secure_link 或 JWT token 验证
  4. CORS 配置:按需配置跨域访问

8. 常见问题

Q1: HLS 播放列表不更新?

A: 确保播放列表响应头禁用缓存:

location ~ \.m3u8$ {
    expires -1;
    add_header Cache-Control "no-cache, no-store, must-revalidate";
}

Q2: MP4 seek 不准确?

A: 启用 mp4_start_key_frame onNGINX Plus 1.21.4+),或使用 edit list 隐藏非关键帧。

Q3: FLV 无法 seek

A: FLV 需要播放器支持,确保播放器发送 start 参数。

Q4: 大文件处理缓慢?

A: 使用 qt-faststart 将 moov atom 移到文件开头:

qt-faststart input.mp4 output.mp4

Q5: 跨域播放失败?

A: 添加 CORS 响应头:

add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";

9. 参考资源