跳转至

live-common: Kaltura 媒体框架公共 NGINX 模块

安装

您可以在任何基于 RHEL 的发行版中安装此模块,包括但不限于:

  • RedHat Enterprise Linux 7、8、9 和 10
  • CentOS 7、8、9
  • AlmaLinux 8、9
  • Rocky Linux 8、9
  • Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-live-common
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-live-common

通过在 /etc/nginx/nginx.conf 顶部添加以下内容来启用模块:

load_module modules/ngx_http_api_module.so;

本文档描述了 nginx-module-live-common v2.1.0,于 2026 年 6 月 11 日发布。


构建状态

一个用于实时视频流的分布式框架。该系统由多个组件组成,每个组件负责特定的功能。

这些组件可以在单个服务器上进行小规模部署/测试,但建议将它们分开部署,以实现更优化的资源利用。例如,转码器可以利用 GPU,因此在支持 GPU 的服务器上部署转码器会更具成本效益,而其他组件则可以在不支持 GPU 的服务器上运行。

媒体在不同组件之间通过内部使用自定义协议传输 - 1. Kaltura 媒体协议 (KMP) - 一种基于 TCP 的协议,用于传输流媒体,概念上类似于单个 fMP4/MPEG-TS 的轨道 2. Kaltura 分段媒体协议 (KSMP) - 一种基于 HTTP 的协议,用于分段传输媒体,概念上是 LLHLS/DASH 的超集

不同媒体组件的编排由“控制器”执行。控制器的主要职责是构建媒体管道的拓扑,并在发生故障时进行更新。控制器通过 HTTP-POST 接收来自媒体组件的 JSON 事件。此外,所有媒体处理组件都公开一个 基于 JSON 的 REST API,控制器使用该 API 获取最新状态并采取行动。一个用于全功能服务器的示例控制器实现提供在 conf 文件夹中。

主要特性

  • 发布协议:RTMP、MPEGTS(通过 SRT/HTTP/TCP)
  • 播放协议:HLS/LLHLS、DASH
  • 实时推送/中继协议:RTMP
  • 视频/音频转码 - 包括基于 ffmpeg API 的 GPU 支持
  • 持久性 - 在 S3(或兼容)对象存储中
  • 自适应比特率传输
  • 字幕支持 - 包括将 608/708 转换为 WebVTT
  • 替代音频
  • 媒体加密和 DRM
  • 视频帧捕获

入门

conf 文件夹包含运行全功能服务器的示例代码和配置。

术语表

  • 频道 - 表示实时流的容器,可能包含轨道、变体、时间线等。
  • 轨道 - 单个视频/音频/字幕的表现形式。例如,一个频道可能有 3 个视频轨道:1080p、720p、540p。
  • 变体 - 用于打包的轨道分组。变体决定在使用多路复用段时,哪个音频轨道将与每个视频轨道配对。 一个变体可以指向多个轨道,但每种媒体类型最多只能有一个轨道。 轨道必须与变体关联,以便通过 HLS/DASH 进行传输。
  • - 特定轨道的一组帧。段始终是独立的 - 视频段总是以关键帧/IDR 帧开始。
  • 段索引 - 识别与特定时间间隔相关联的不同轨道的段的编号。
  • 周期 - 一组可以连续播放的段索引。
  • 时间线 - 一组周期。可以创建多个时间线,每个时间线都有自己的一组周期。 时间线可以用于实现“预览模式” - 发布者消费一个时间线,而观众消费另一个时间线。 发布者的时间线始终是 active,而观众的时间线在发布者的决定下激活。

示例拓扑

下面的图示展示了一些可以使用媒体框架组件创建的示例拓扑。

简单 RTMP 直通

flowchart LR;
    enc(编码器);
    ingest(nginx-rtmp-kmp-module);
    live(nginx-live-module);
    pckg(nginx-pckg-module);
    play(播放器);
    enc-->|RTMP|ingest;
    ingest-->|KMP|live;
    live-->|KSMP|pckg;
    pckg-->|LLHLS/DASH|play;

直通 + S3 持久性

flowchart LR;
    enc(编码器);
    ingest(nginx-rtmp-kmp-module);
    live(nginx-live-module);
    pckg(nginx-pckg-module);
    s3(Amazon S3);
    play(播放器);
    enc-->|RTMP|ingest;
    ingest-->|KMP|live;
    live-->|HTTP|s3;
    s3-->|HTTP|live;
    live-->|KSMP|pckg;
    pckg-->|LLHLS/DASH|play;

SRT 输入 + 视频转码

flowchart LR;
    enc(编码器);
    ingest(nginx-mpegts-kmp-module);
    srt(nginx-srt-module);
    trans(转码器);
    live(nginx-live-module);
    pckg(nginx-pckg-module);
    play(播放器);
    enc-->|SRT|srt;
    srt-->|MPEG-TS|ingest;
    ingest-->|KMP 视频|trans;
    trans-->|KMP 视频|live;
    ingest-->|KMP 音频|live;
    live-->|KSMP|pckg;
    pckg-->|LLHLS/DASH|play;

字幕解码

flowchart LR;
    enc(编码器);
    ingest(nginx-rtmp-kmp-module);
    cc(nginx-cc-module);
    live(nginx-live-module);
    pckg(nginx-pckg-module);
    play(播放器);
    enc-->|RTMP|ingest;
    ingest-->|KMP 视频|cc;
    cc-->|KMP 字幕|live;
    ingest-->|KMP 视频|live;
    ingest-->|KMP 音频|live;
    live-->|KSMP|pckg;
    pckg-->|LLHLS/DASH|play;

转码 + RTMP 推送

flowchart LR;
    enc(编码器);
    ingest(nginx-mpegts-kmp-module);
    trans(转码器);
    live(nginx-live-module);
    pckg(nginx-pckg-module);
    push(nginx-kmp-rtmp-module);
    yt(YouTube);
    play(播放器);
    enc-->|MPEG-TS/HTTP|ingest;
    ingest-->|KMP|trans;
    trans-->|KMP|live;
    trans-->|KMP|push;
    push-->|RTMP|yt;
    live-->|KSMP|pckg;
    pckg-->|LLHLS/DASH|play;

组件概述

媒体组件

  • nginx-rtmp-kmp-module - 实时媒体摄取,输入:RTMP,输出:KMP x N

  • nginx-mpegts-kmp-module - 实时媒体摄取,输入:MPEG-TS over TCP/HTTP,输出:KMP x N

  • 转码器 - 视频/音频转码,输入:KMP,输出:KMP x N

  • nginx-live-module - 实时媒体分段器,输入:KMP x N,输出:KSMP

    附加功能:持久性、填充、时间线支持。

  • nginx-pckg-module - 实时媒体打包器(无状态),输入:KSMP,输出:HLS/LLHLS, DASH

    附加功能:自适应比特率、字幕、替代音频、媒体加密 / DRM、视频帧捕获

  • nginx-kmp-cc-module - 字幕解码器,输入:KMP 视频 (h264/5),输出:KMP 字幕 (WebVTT) x N

  • nginx-kmp-rtmp-module - 实时媒体中继,输入:KMP x N,输出:RTMP

重要:所有有状态的基于 nginx 的组件(=除 nginx-pckg-module 外的所有组件)必须部署在单个进程的 nginx 服务器上(worker_processes 1;)。 模块状态是按进程保持的,当使用多个进程时,无法控制哪个进程将处理请求。 例如,创建分段器上的频道的请求可能到达工作进程 1,而与实际媒体的 KMP 连接将到达工作进程 2。 在使用容器的部署中,这不应成为问题 - 可以在单个服务器上部署多个容器,而不是使用多个 nginx 进程。 另一种可能性是使用像 arut 的 per-worker listener 这样的补丁, 但它可能需要更新以适用于 stream 连接。

调试选项

一些媒体框架组件支持用于调试目的的可选预处理器宏 - - NGX_LBA_SKIP (nginx-common) - 跳过使用“大缓冲区数组”(LBA)模块。当启用时,LBA 分配将路由到 ngx_alloc / ngx_free。 - NGX_RTMP_VERBOSE (nginx-rtmp-module) - 启用额外的调试日志消息 - NGX_LIVE_VALIDATIONS (nginx-live-module) - 启用对内部数据结构的运行时一致性检查,使用 --with-debug 时默认启用 - NGX_BLOCK_POOL_SKIP (nginx-live-module) - 跳过使用块池。当启用时,块池分配将路由到 ngx_palloc / ngx_pfree。

要使用 valgrind 测试模块,建议应用 no-pool-nginx 补丁, 并使用 --with-cc-opt="-O0 -DNGX_BLOCK_POOL_SKIP -DNGX_LBA_SKIP"--with-debug 配置 nginx。

Kaltura 媒体协议 (KMP)

Kaltura 媒体协议是一种简单的基于数据包的协议,用于通过 TCP 传输媒体。 KMP 连接可以传输单个视频/音频/字幕轨道的媒体 - 当需要多个轨道时,会建立多个 TCP 连接。

每个数据包以包含以下字段的头部开始(每个字段 32 位) - - 类型 - 数据包的类型。数据包类型是四个字符的代码,下面是当前定义的类型列表。 - 头部大小 - 数据包头部的大小,必须在 sizeof(kmp_packet_header_t) 和 64KB 之间。 解析器必须使用头部大小来访问数据包的数据,这使得可以在不破坏现有解析器的情况下向数据包头部添加新字段。 - 数据大小 - 数据包数据的大小,必须在 0 和 16MB 之间。 - 保留 - 保留供将来使用,必须设置为 0。

KMP 中使用的结构和常量可以在 ngx_live_kmp.h 中找到。

KMP 帧 ID

帧 ID 是一个 64 位整数,唯一标识输入帧。 摄取模块(nginx-rtmp-kmp-module / nginx-mpegts-kmp-module)根据服务器时钟(以时间尺度单位)分配初始帧 ID, 当创建输出轨道时。 为了避免在每个发送的帧上都需要发送帧 ID,KMP 中的帧 ID 是顺序的 - 发送的第 N 帧的 ID 为 initial_frame_id + N

如果输入连接(例如 RTMP)掉线并重新建立,将分配新的 KMP 帧 ID。 由于默认时间尺度较高(90kHz),而帧速率不太可能超过 60fps,即使在短时间流媒体后重新连接, 初始帧 ID 也将显著高于上一个连接中最后发送的帧 ID。 因此,由于重新连接而与任何先前使用的帧 ID 发生冲突的可能性极小。

帧 ID 用于: - 识别在 KMP ack 数据包中被确认的帧 - 如果 KMP 连接重新建立,跳过先前处理的帧

转码器为帧 ID 的管理增加了一些复杂性 - - 由于转码器可能会改变输入帧速率或丢弃帧,转码器输入中的帧 ID, 不一定与转码器输出中的帧 ID 相同。 如果转码器重新启动,它需要知道要发送的上游服务器的 initial_frame_id 的值(通常是 nginx-live-module)。 upstream_frame_id 字段用于此目的。 - 转码器可能被配置为改变音频轨道的采样率,在这种情况下,转码帧与输入帧不对齐。 为了处理这种情况,转码器需要能够仅确认输入帧的一部分。 这就是 offset 字段的目的 - 它可以存储,例如,应该在帧内确认的音频样本的数量。 offset 字段的确切含义由 KMP 接收方确定 - 接收方在返回的 ack 帧中设置 offset,并在重新连接时在连接数据包的 initial_offset 字段中返回。

发布者 KMP 数据包

以下部分列出了 KMP 发布者可以发送的 KMP 数据包。

连接 (cnct)

在 KMP TCP 连接建立后立即发送。

头部包含以下字段: - channel_id - 字符串,正在发布的频道 ID。最大允许长度为 32 个字符。 - track_id - 字符串,正在发布的轨道 ID。最大允许长度为 32 个字符。 - initial_frame_id - 整数,正在发送的第一帧的 ID。 - initial_upstream_frame_id - 整数,应发送到上游服务器的初始帧 ID(由转码器使用) - initial_offset - 整数,初始帧内的起始偏移量。 - flags - 整数,标志的位掩码,目前定义了一个标志 - consistent - 当 KMP 发布者生成一致(逐位精确)输出时设置此标志,给定相同的输入。 nginx-rtmp-kmp-modulenginx-mpegts-kmp-module 是一致的发布者的示例。 而 转码器 则不是。consistent 标志在重新连接时由 LL 分段器使用。 当发布者一致时,LL 分段器可以从它停止的地方继续。 当发布者不一致时,LL 分段器只能从下一个 GOP 继续 - 它必须不将断开连接前的部分 GOP 与断开连接后接收到的 GOP 混合。

连接数据包的数据是可选的,数据的预期格式由特定的 KMP 接收方定义。

媒体信息 (minf)

包含媒体的参数。 头部中的某些字段由所有媒体类型共享,而其余字段仅为特定类型定义(联合)。

共享的头部字段包括: - media_type - 整数,媒体类型 - video / audio / subtitle,使用 KMP_MEDIA_XXX 常量。 - codec_id - 整数,编码器 ID,使用 KMP_CODEC_XXX 常量。 - timescale - 整数,帧数据包中 dts / pts_delay / created 字段使用的单位,以 Hz 为单位。 - bitrate - 整数,比特率,以每秒比特为单位。

视频特定的头部字段包括: - width - 整数,视频宽度,以像素为单位。 - height - 整数,视频高度,以像素为单位。 - frame_rate - 有理数,视频的帧速率,以每秒帧数为单位。 - cea_captions - 布尔值,当视频轨道包含 EIA-608 / CTA-708 字幕时设置为 1

音频特定的头部字段包括: - channels - 整数,音频通道的数量。 - bits_per_sample - 整数,音频样本的大小,以比特为单位。 - sample_rate - 整数,音频的采样率,以每秒样本数为单位。 - channel_layout - 整数,通道位置的位掩码,使用 KMP_CH_XXX 常量。

媒体信息数据包的数据包含编码器的私有/额外数据。 例如,当使用 h264 编码器时,数据包含 avcC MP4 盒的主体。

KMP 接收方应处理媒体信息的变化,例如,视频分辨率的变化。 然而,在 KMP 连接中发送的媒体类型(视频/音频/字幕)不得改变。

KMP 接收方应忽略与先前接收到的媒体信息数据包相同的媒体信息数据包。

帧 (fram)

表示单个视频帧/音频帧/字幕提示。

帧头部包含以下字段: - created - 整数,帧被管道中第一个媒体框架模块接收的时间,以时间尺度单位表示。 - dts - 整数,帧的解码时间戳,以时间尺度单位表示。 当媒体类型为 subtitle 时,保存提示的开始时间戳。 - flags - 整数,目前仅定义一个标志 - key - 在视频关键帧上启用。 - pts_delay - 整数,帧的呈现时间戳与解码时间戳之间的差异,以时间尺度单位表示。 当媒体类型为 subtitle 时,保存提示的持续时间 - end_pts - start_pts

当媒体类型为视频/音频时,帧数据包的数据包含压缩媒体。 当媒体类型为字幕且编码器为 WebVTT 时,帧的数据遵循 WebVTT 样本格式,如 ISO/IEC 14496-30 中所规定 (通常在这种情况下,样本是一个 vttc 盒,包含一个 payl 盒)。

空 (null)

用于发出“实时”信号,防止空闲计时器过期。 空数据包不携带除基本 KMP 头部以外的任何数据。 解析器必须忽略空数据包。

流结束 (eost)

用于发出发布会话的优雅终止信号。 流结束数据包不携带除基本 KMP 头部以外的任何数据。

接收者 KMP 数据包

以下部分列出了 KMP 接收者可以发送的 KMP 数据包。

确认帧 (ackf)

确认接收到的帧。

KMP 接收方决定发送 ack 数据包的适当时机。 例如,当启用持久性时,分段器仅在包含帧的段保存到存储后发送 ack。

某些接收方根本不发送 ack,在这种情况下,KMP 生产者必须配置为在发送后丢弃帧(使用 resume_from 设置)

数据包头部包含以下字段: - frame_id - 整数,应该重新发送的第一帧的 ID,如果连接掉线。 换句话说,ack 帧数据包确认所有 ID 小于 frame_id 的帧。 如果 KMP 连接重新建立,此值将发送在 initial_frame_id 字段中。 - upstream_frame_id - 整数,发送到上游服务器的帧的 ID。 如果 KMP 连接重新建立,此值将发送在 initial_upstream_frame_id 字段中。 - offset - 整数,帧内的确认偏移量。 如果 KMP 连接重新建立,此值将发送在 initial_offset 字段中。 - padding - 整数,保留供将来使用,必须设置为零。

ack 数据包的数据不被使用。

Kaltura 分段媒体协议 (KSMP)

Kaltura 分段媒体协议是一种基于 HTTP 的协议,用于分段传输媒体,类似于 HLS/DASH。

KSMP 请求是一个 HTTP GET 请求,定义了以下查询参数 - - channel_id - 必需的字符串,频道的 ID - timeline_id - 必需的字符串,时间线的 ID - flags - 必需的十六进制整数,标志: - 选择所需数据的子集(如 SQL SELECT 语句中的列列表) - 控制服务请求时的各种行为。 例如,“最近关键帧”标志,仅返回最接近请求时间戳的关键帧,而不是返回整个段。 - variant_ids - 可选字符串,选择应返回的变体子集,默认情况下返回所有变体。 如果指定多个变体,它们应以短横线(-)分隔。 - media_type_mask - 可选十六进制整数,设置应返回的媒体类型,默认情况下返回所有媒体类型。 - time - 可选整数,请求的时间戳。时间戳用于,例如,在特定时间捕获视频帧。 - segment_index - 可选整数,段的索引 - max_segment_index - 可选整数,用于限制响应中返回的段的范围。此参数可用于调试时重放持久化流。 - part_index - 可选整数,段内部分段的零基索引。使用 part_index 的请求必须同时发送 segment_index。 - skip_boundary_percent - 可选整数,将 skip boundary 值设置为 target duration 的百分比 (有关更多详细信息,请参见 HLS 规范中 CAN-SKIP-UNTIL 属性的定义) - padding - 可选整数,在响应末尾添加额外的零字节。用于遵守 ffmpeg 的填充要求,而不产生额外的复制操作。

KSMP 响应使用 KLPF 格式(见下文),类型为 Serve (serv)。 KSMP 特定的定义可以在 ngx_ksmp.h 中找到。

Kaltura 实时持久文件 (KLPF)

Kaltura 实时持久文件是一种序列化方案,用于 KSMP 响应和 nginx-live-module 创建的 S3 对象中。

KLPF 由块组成,类似于 MP4 原子/盒。每个块都有以下头部 - - id - 四个字符的代码,标识块 - size - uint32,块的完整大小(头部和数据) - flags - 4 位,定义了以下标志: - container (0x1) - 块包含其他块 - index (0x2) - 块是另一个块的索引,头部大小不应使用 - compressed (0x4) - 块的数据经过 zlib 压缩 - header_size - 28 位,块头部的大小。解析器必须使用头部大小来访问块的数据, 以便可以在不破坏兼容性的情况下向头部添加字段。

KLPF 文件是一个 ID 设置为 klpf 的块。 在通用块头部字段(如上所列)之后,KLPF 文件的头部具有以下字段 - - uncomp_size - uint32,保存未压缩数据的大小,当 KLPF 数据被压缩时 - version - uint32,文件格式的版本。用于新文件的版本在每次格式发生重大变化时更新,代码将更新为 - 支持读取新格式和旧格式,或 - 忽略使用旧格式的文件 - type - 四个字符的代码,标识存储在 KLPF 中的数据类型。类型决定支持哪些块 ID 及其内部结构。 类型 serv (Serve) 用于 KSMP 响应,在打包器和分段器之间的通信中。分段器内部使用其他类型。 - created - uint64,KLPF 创建时的 unix 时间戳。

有关 KLPF 块内部结构的更多详细信息,请参见 KLFP-SPEC.md

要检查 KLPF 对象/KSMP 响应的内容,请使用 klpf_parse.py。 该脚本可以显示块结构而无需任何附加信息,但要解析块内部的字段: - 运行 generate_persist_spec.py,并将输出保存到文件中 - 使用 -s / --spec-file 选项将文件名提供给 klpf_parse.py。

API 概述

所有媒体处理组件都公开一个基于 JSON 的 REST API。 本节解释媒体框架 API 的一般属性。 有关可用 API 端点的详细参考,请参见特定模块的文档。

请求类型

API 中使用以下 HTTP 动词: - GET - 获取模块的完整状态或其子集。可以将参数 ?pretty=1 添加到请求,以返回“美化”/缩进格式的响应。 - GET?list=1 - 返回 API 中某个路径下“文件夹”的名称。可用于遍历可能的 API 路由树。 - POST - 创建对象。 - PUT - 更新对象,更新的对象 ID 通过 URI 传递。 - DELETE - 删除对象,删除的对象 ID 通过 URI 传递。

POST / PUT 请求中的请求体必须是 JSON(通常是对象),并且请求必须使用头部 Content-Type: application/json

当请求体的大小超过某个阈值时,nginx 会将其写入临时文件。 然而,媒体框架 API 的实现要求 POST / PUT 请求的请求体必须在内存中可用。 如有需要,可以使用 nginx 的 client_body_buffer_size 指令来增加为请求体分配的缓冲区大小。

多请求

在 nginx-live-module 中设置频道可能需要多个 API 调用 - 创建频道、创建时间线、创建变体等。 为了避免多次往返的惩罚,API 层支持“多”请求。 多请求将多个 API 请求捆绑在一个 HTTP 请求中。

多请求必须使用 POST 动词,其 URI 必须设置为 /multi。 请求体必须是一个 JSON 对象数组,每个对象表示一个单独的 API 请求。

对象包含以下字段: - uri - 字符串,必需,相对 API 路径。 - method - 字符串,必需,请求的 HTTP 动词 - GET / POST / PUT / DELETE。 - body - 任意(通常是对象),可选,POST / PUT 请求的主体。

多请求的响应也是一个 JSON 对象数组。 响应数组中的元素数量始终与请求数组中的元素数量匹配, 响应数组中对象的顺序与请求数组中的顺序匹配。 换句话说,响应数组的第 N 项是请求数组中第 N 个请求的响应。

每个响应对象包含以下字段: - code - 整数,必需,HTTP 状态码 - body - 任意(通常是对象/数组),可选,响应体

GitHub

您可以在 GitHub repository for nginx-module-live-common 中找到此模块的其他配置提示和文档。