H.264/TS 预处理重复 PTS 导致 GOP 标记丢失的运行时修复补丁
查找文件
2026-09-03 10:19:56 +08:00
libvax_duplicate_gop_fix.so Publish GOP marker runtime fix 2026-09-03 10:19:56 +08:00
NtsVaxCreatorServer.ldpreload-wrapper Publish GOP marker runtime fix 2026-09-03 10:19:56 +08:00
README.md Publish GOP marker runtime fix 2026-09-03 10:19:56 +08:00

视频预处理时长异常修复说明

1. 项目结论

本目录提供 NtsVaxCreatorServer 的生产应急修复补丁,用于解决部分业务 H.264/TS 视频生成 .nix 索引时只记录开头少量帧,进而表现为播放时长只有几秒或时长异常的问题。

问题本质不是视频整体损坏,而是特定 H.264 码流结构触发了原预处理程序的兼容性缺陷:异常视频的 GOP/关键帧附近通常先出现 SEI,随后才出现 SPS/PPS,同时存在 B 帧、DTS 和解码顺序下非单调的 PTS。原程序按 PTS 去重视频 payload 时,会把“后到达但 PTS 相同”的 GOP 标记一起丢弃,导致后续 GOP/帧索引无法完整写入 .nix

最终方案通过 LD_PRELOAD 在运行时修正 CNsTsVaxWriter::AddVideoPayload 的重复 PTS 分支,把遗漏的 GOP 标记补到已经写入的帧记录中,并继续调用原有 GOP 索引逻辑。补丁:

  • 不修改输入 TS;
  • 不生成临时完整 TS;
  • 不改变 .nix 输出路径或格式;
  • 不改变后端调用路径,仍调用 /usr/local/sbin/NtsVaxCreatorServer
  • 对正常样例不做额外修正,验证中 fixed=0

现有材料记录表明,该补丁已通过 CentOS 7 兼容构建和异常/正常样例验证,并解决了本次报告的生产问题。它属于无源码条件下的运行时热修复,不等同于厂商源码级修复。

2. 最终交付物

当前目录中的生产部署文件如下:

文件 大小 用途
libvax_duplicate_gop_fix.so 16,360 字节 运行时 GOP 标记修复库
NtsVaxCreatorServer.ldpreload-wrapper 391 字节 保持原调用路径不变的 shell 启动包装器
README.md - 本说明文档

SHA-256:

libvax_duplicate_gop_fix.so
B568C5CC1CBA862CCA69FF552C194912C485A5D7B60AF5B354BE03F320D3D646

NtsVaxCreatorServer.ldpreload-wrapper
568847587BB4028FBA5E13A201263288B314C358429F4ABC1E52919DA3C07659

3. 现象与根因

3.1 码流侧特征

异常样例和转码后的正常样例均为 1920×1080、25 fps、H.264 High Profile,但关键差异如下:

特征 异常样例 正常/转码样例
H.264 level 4.2 4.0
B 帧 has_b_frames=2 has_b_frames=0
DTS 存在 未报告
PTS 在 PES/解码顺序下非单调 单调
典型关键帧 NAL 顺序 AUD → SEI → SEI → SEI → SPS → PPS → IDR AUD → SPS → PPS → SEI → … → IDR
FFmpeg 解码 可成功解码 可成功解码

NAL 类型对应关系:9=AUD6=SEI7=SPS8=PPS5=IDR。异常视频可见 FFmpeg 警告:

non-existing SPS 0 referenced in buffering period

这说明 SEI 中引用了尚未出现的 SPS,但播放器和 FFmpeg 通常仍能容错,因此不能简单认定原视频整体损坏。

3.2 原程序失效链路

  1. 正常视频 payload 先以某个 PTS 到达并写入环形缓冲区。
  2. 含 GOP 标记的后续 payload 使用相同 PTS 到达。
  3. CNsTsVaxWriter::AddVideoPayload 将其视为重复 PTS 并提前返回。
  4. GOP 标记随 payload 被丢弃,AddGopIndex 无法持续生成索引。
  5. .nix 仅保存开头少量帧/GOP,业务侧看到时长异常或播放提前停止。

3.3 决定性实验

对一组异常样例截取 10 秒,在不转码的前提下进行对照:

处理方式 .nix 大小 帧数 GOP 数
仅 remux,保留 SEI 192 字节 27 2
仅移除 H.264 SEI 480 字节 252 11

仅 remux 没有改善,移除 SEI 后索引恢复,证明触发点来自 H.264 SEI/SPS 排列及原写入器的后续处理,而不是 MPEG-TS 容器整体损坏。

4. 补丁工作方式

wrapper 在启动原程序前设置:

export LD_BIND_NOW=1
export LD_PRELOAD="/usr/local/lib/libvax_duplicate_gop_fix.so${LD_PRELOAD:+:$LD_PRELOAD}"
exec /usr/local/sbin/NtsVaxCreatorServer.real "$@"

LD_BIND_NOW=1 必须保留。补丁会修改目标库的 GOT 槽;立即绑定可避免延迟绑定稍后写回已恢复为只读的 GOT 页面而引发崩溃。

补丁库的核心逻辑为:

  1. 定位 libnstsvaxcreator.so 及其动态符号表;
  2. CNsTsVaxWriter::AddVideoPayload 的 GOT 槽指向补丁函数;
  3. 非 GOP 标记或非重复 PTS 情况继续调用原函数;
  4. 命中“相同 PTS 的 GOP 标记 payload”时,找到上一条已写入记录并补写标记;
  5. 对首个标记也被丢弃的边界场景,为第一条缓冲帧合成初始标记;
  6. 更新标记计数并调用原 AddGopIndex,不走原来的重复数据丢弃路径。

为兼容 CentOS 7,最终实现不调用可能被链接为 dlsym@GLIBC_2.34dlsym,而是通过 dl_iterate_phdr 解析 ELF 的 DT_SYMTABDT_STRTABDT_HASH/DT_GNU_HASH。交付 .so 只要求 GLIBC_2.2.5,可运行于 CentOS 7 的 glibc 2.17。

5. 适用条件与上线前检查

适用环境:

  • Linux x86-64;
  • 已验证目标为 CentOS 7;
  • 后端调用形式为 /usr/local/sbin/NtsVaxCreatorServer <input.ts> <output.nix>
  • 目标机上的原始 NtsVaxCreatorServerlibnstsvaxcreator.so 与本次分析版本一致。

上线前先记录现网文件信息,至少保存哈希、文件类型和依赖版本:

sudo sha256sum /usr/local/sbin/NtsVaxCreatorServer \
  /usr/local/lib/libnstsvaxcreator.so
file /usr/local/sbin/NtsVaxCreatorServer
ldd /usr/local/sbin/NtsVaxCreatorServer
readelf --version-info ./libvax_duplicate_gop_fix.so | grep GLIBC

最后一条应只看到兼容的低版本 GLIBC 需求,不应出现 GLIBC_2.34。如厂商已更新原程序或 libnstsvaxcreator.so,不要直接复用本补丁,应重新核对结构偏移、符号和全量样例。

建议先在单台预处理节点灰度,并准备一组异常、正常视频各至少一条进行回归。

6. 生产部署

以下命令假定当前目录中已有两个交付文件。先确认尚未存在同名备份;如果 .real 已存在,应先查明它是否是原始程序,避免覆盖有效备份。

test -x /usr/local/sbin/NtsVaxCreatorServer
test ! -e /usr/local/sbin/NtsVaxCreatorServer.real

备份原程序并安装补丁:

sudo install -m 0755 \
  /usr/local/sbin/NtsVaxCreatorServer \
  /usr/local/sbin/NtsVaxCreatorServer.real

sudo install -m 0755 \
  ./libvax_duplicate_gop_fix.so \
  /usr/local/lib/libvax_duplicate_gop_fix.so

sudo install -m 0755 \
  ./NtsVaxCreatorServer.ldpreload-wrapper \
  /usr/local/sbin/NtsVaxCreatorServer

部署后的目录应为:

/usr/local/sbin/NtsVaxCreatorServer        # shell wrapper,后端继续调用此路径
/usr/local/sbin/NtsVaxCreatorServer.real   # 原始二进制
/usr/local/lib/libvax_duplicate_gop_fix.so # 修复库

检查文件及权限:

file /usr/local/sbin/NtsVaxCreatorServer
file /usr/local/sbin/NtsVaxCreatorServer.real
ls -l /usr/local/sbin/NtsVaxCreatorServer \
  /usr/local/sbin/NtsVaxCreatorServer.real \
  /usr/local/lib/libvax_duplicate_gop_fix.so

/usr/local/sbin/NtsVaxCreatorServer 应显示为 POSIX shell script,三个文件均应具有 0755 权限。

7. 上线验证

用一条异常样例和一条正常样例分别生成 .nix,保持原业务调用参数不变:

/usr/local/sbin/NtsVaxCreatorServer abnormal.ts abnormal.nix
/usr/local/sbin/NtsVaxCreatorServer normal.ts normal.nix

短时开启补丁诊断:

VAX_GOP_FIX_DEBUG=1 \
  /usr/local/sbin/NtsVaxCreatorServer abnormal.ts abnormal.debug.nix \
  2>abnormal.debug.log

grep 'vax_gop_fix: calls=' abnormal.debug.log

典型摘要为:

vax_gop_fix: calls=... fixed=...
  • 异常样例预期 fixed>0
  • 正常样例预期 fixed=0
  • 生产常态不需要设置 VAX_GOP_FIX_DEBUG

原程序在本地测试中经常返回 rc=1,即使生成的 .nix 完全有效,因此不能只根据进程返回码判断成功。还应检查 .nix 是否存在、大小是否合理,以及头部的帧数和 GOP 数。该格式在现有分析脚本中按以下偏移读取:

  • 0x28–0x2B:小端 frame_count
  • 0x2C–0x2F:小端 gop_count

最终应同时确认:异常视频播放时长恢复、正常视频无回归、后端仍能关联原输入和输出路径。

8. 已完成验证

8.1 四组 10 秒样例

以下数据来自归档的 .nix 头部及调试日志:

样例 原程序异常样例 帧/GOP 补丁后异常样例 帧/GOP 补丁后正常样例 帧/GOP 异常样例 fixed
样例 A 0 / 0 251 / 11 250 / 5 10
样例 B 2 / 1 252 / 11 250 / 5 10
样例 C 27 / 2 252 / 11 250 / 5 9
样例 D 2 / 1 252 / 11 250 / 5 10

4 组正常样例均为 fixed=0,说明补丁没有误命中正常流。

8.2 完整样例 D

样例 .nix 大小 帧数 GOP 数 fixed
异常原流 + 补丁 4,128 字节 3,108 125 124
正常转码流 + 补丁 2,144 字节 3,108 63 0

两者帧数完全一致。异常原流 GOP 数较多,是因为原流 GOP 间隔比转码后的正常流短,并非补丁重复生成帧。

9. 回滚

如灰度发现异常,立即恢复原程序并移除修复库:

sudo test -x /usr/local/sbin/NtsVaxCreatorServer.real
sudo install -m 0755 \
  /usr/local/sbin/NtsVaxCreatorServer.real \
  /usr/local/sbin/NtsVaxCreatorServer
sudo rm -f /usr/local/lib/libvax_duplicate_gop_fix.so

确认恢复后的 NtsVaxCreatorServer 已重新显示为 ELF 二进制,并用正常样例做一次回归。建议保留 .real 备份直到灰度和观察期结束;确认无需再次启用补丁后再人工清理。

10. 常见问题

wrapper 报缺少文件并返回 127

wrapper 会主动检查以下两个文件:

/usr/local/lib/libvax_duplicate_gop_fix.so
/usr/local/sbin/NtsVaxCreatorServer.real

检查文件是否存在、可读/可执行,以及权限是否为 0755

GLIBC_2.34 not found

这通常表示误用了早期在 Ubuntu 24 上编译的 ELF launcher 或旧补丁。最终交付使用 shell wrapper,当前 .so 仅要求 GLIBC_2.2.5。替换为本目录中的两个最终文件,不要使用旧的 NtsVaxCreatorServer.ldpreload-launcher

部署后没有出现修复效果

  1. file 确认原路径已经是 shell wrapper;
  2. VAX_GOP_FIX_DEBUG=1 检查是否输出 calls/fixed
  3. 确认 LD_BIND_NOW=1 没有从 wrapper 中移除;
  4. 检查 SELinux、systemd 或安全启动器是否清理/禁止 LD_PRELOAD
  5. 核对现网原程序和 libnstsvaxcreator.so 是否已被厂商更新。

.nix 已生成但程序返回 1

这是原程序在现有样例上的已知行为。应以 .nix 头部帧数/GOP 数、文件大小和实际播放验证为主,不能仅看返回码。

11. 方案边界与风险

  • 补丁依赖当前 NtsVaxCreatorServer/libnstsvaxcreator.so 的符号和内存布局;厂商升级后二进制偏移可能变化。
  • 补丁只修复“重复 PTS 导致 GOP 标记丢失”这一类问题,不能覆盖所有损坏 TS 或其他编码异常。
  • LD_PRELOAD 可能被 SELinux、受限执行环境或安全包装器禁用。
  • 这是生产应急/灰度方案,长期方案仍应优先推动厂商在源码中修正写入器的重复 PTS 处理,并补充回归测试。
  • 不建议把“移除 SEI 后生成完整临时 TS”作为生产主方案:它会增加预处理磁盘占用,并可能破坏后端/数据库对原文件与索引的关联。