跳转到主要内容

crayonxiaoxin

HLS 广告过滤启发式算法:假阳性问题与修复

前言

Yam TV 支持通过本地 HTTP 代理过滤 HLS 流中的广告分段。广告检测算法使用启发式方法分析分段时长和重复模式。但在实际运营中发现,这条启发式会把普通视频内容误判为广告(假阳性),导致播放时正片被错误删除。

问题场景

以下是控制台输出的诊断日志:

块 3 | 重复次数: 1 | 时长: 32.2s | 占比: 4.78% | 判定: 【广告】
块 4 | 重复次数: 1 | 时长: 30.0s | 占比: 4.46% | 判定: 【广告】
...(共 22 个连续块全部被标记)
准备删除 440 个相关行

块 3 到块 24,每个块 28-32 秒,连续 22 个块总时长 672 秒——这显然是正片内容,不是广告。但启发式算法将其全部标记为广告。

启发式算法分析

原有判定逻辑:

if (repeatTimes >= 3) -> 广告
else if (repeatTimes == 2) -> 广告(时长 < 180s)
else {
  // repeatTimes == 1,唯一出现
  if (segs.length < 2) -> 不是广告
  if (ratio < 15% && duration < 90s) -> 广告
}

每个块在 672 秒视频中占比约 4.5%(小于 15% 阈值),时长约 30 秒(小于 90 秒阈值),且出现次数为 1(不重复)。三个条件全命中 → 判定为广告。

但这是正常的连续视频内容——每段 30 秒、不重复、连续排列。算法将连续视频中的每一段独立看待,用「占比小 + 时长短 + 不重复」三合一误判为广告。

根因:比例检查的误用

问题在于对唯一出现(repeatTimes == 1)的块使用「时长占比」和「绝对时长」作为判定标准。30 秒的正片分段在长视频中占比必然小,而这个比例对于唯一出现的块没有意义——它不去判断「这个块是不是广告」,而是误判了「这个块占比很小所以可能是广告」。

正确的标准应该是唯一出现的短块(< 15 秒)才可能是广告,正常内容的分段时长通常至少在 15-60 秒范围。

修复方案

去除 repeatTimes == 1 的比例检查和短块时长检查,改为简单的短时间检查:

// 修复后
if (repeatTimes >= 3) -> 广告
else if (repeatTimes == 2) -> 广告(时长 < 180s)
else {
  // 唯一出现:只有极短的块(< 15s)才可能是广告
  heuristicAd = b.totalDurationSec < 15.0;
}

同时添加了「规则命中后跳过启发式」的保护。原则是:一个视频流不应该同时被规则引擎和启发式判定为广告。如果管理员配置的规则已经命中了一些分段,说明这个流确实有广告模式,不需要启发式再补充诊断:

// 如果 Block 规则已经命中了任何分段,直接跳过启发式
if (hasMatchingBlockRule) {
  skipHeuristic = true;
}

两种修复同时生效

  • 无规则环境:启发式运行,30 秒正片块因 > 15 秒不会被误判;真正的广告块(通常 5-10 秒)仍能被捕获
  • 有规则环境:规则命中后启发式被跳过,完全由管理员配置的规则决定

总结

启发式算法的核心假设是「异常片段 ≈ 广告」。但 HLS 流中正常的 30 秒分段也会在长视频中表现出「异常」特性(占比小、不重复)。修复的关键是认识到不同模式需要不同的判定标准——重复多次的块可能是广告,但唯一出现的块只有在极短的情况下才可能是广告。

讨论

还没有留言,来留下第一条评论吧!

留下足迹