Skip to content

Latest commit

 

History

History
170 lines (123 loc) · 8.44 KB

File metadata and controls

170 lines (123 loc) · 8.44 KB

需求对齐文档:视频 Block 生成流程 (Map Phase)

本文档旨在梳理我对 "Atomic Block"(原子视频块)生成流程的理解。请校验以下逻辑是否符合你的预期。

1. Block 的核心结构

一个标准的 Block 对应一个用户选中的视频片段(Clip)。 Block 的总时长 = Head + Body + Tail

时序图解

| <--- Head ---> | <--- Body ---> | <--- Tail ---> |
| (Transition)   | (Main Content) | (Fade Out)     |

各部分定义

A. Head (片头/转场区)

Head 的总时长 = 转场音频时长 (transitionDuration) + 淡入时长 (fadeInDuration)。

Head 内部又分为两段:

  1. 纯背景段 (Pure Background):

    • 时长: 等于 transitionDuration
    • 画面: 仅显示背景视频(根据全局时间轴裁切)。正片画面不可见。
    • 音频: 播放转场音效(如 "Whoosh")。
    • 逻辑: 如果没有转场音频,此段时长为 0。
  2. 淡入段 (Fade In):

    • 时长: 等于 fadeInDuration
    • 画面: 正片从第 0 秒开始播放,透明度从 0 渐变到 1。底层显示背景视频。
    • 音频: 正片音频从静音渐变到 100%。
    • 消耗: 此段消耗正片的前 fadeInDuration 秒内容。

B. Body (正片主体)

  • 时长: Clip原始时长 - fadeInDuration - fadeOutDuration
  • 画面: 正片完全不透明(Alpha=1)。
  • 音频: 正片原声。
  • 消耗: 消耗正片中间段内容。

C. Tail (片尾/淡出区)

  • 时长: 等于 fadeOutDuration
  • 画面: 正片透明度从 1 渐变到 0。底层显示背景视频(此背景是为下一个 Block 做铺垫的)。
  • 音频: 正片音频从 100% 渐变到静音。
  • 消耗: 消耗正片的最后 fadeOutDuration 秒内容。

2. 关键逻辑 QA (请校验)

以下是我对现有代码逻辑的理解,请指出是否与你的需求有出入。

Q1: 转场音频是否会占用正片时长?

我的理解: 不会。 转场音频(如 2秒)会额外增加 Block 的总时长。 例如:正片 10秒,淡入 1秒,转场音频 2秒。

  • Block 总长 = 2s (纯背景) + 10s (正片本身) = 12s。
  • 前 2s 是纯背景 + 转场音。
  • 第 3s 是正片开始淡入。

回答:你的理解正确

Q2: 背景视频是如何对齐的?

我的理解: 背景视频是连续的。 系统计算了全局的累积时间 (accumulatedTime)。

  • Block N 的 Head 背景,取自背景视频的 accumulatedTime 时刻。
  • Block N 的 Tail 背景,取自背景视频的 accumulatedTime + BlockDuration 时刻。
  • 这样当 Block N 和 Block N+1 拼接时,背景画面是平滑连续的。

回答:你的理解正确

Q3: 如果正片非常短(短于淡入+淡出)怎么办?

我的理解:这是边缘情况。

  • 目前逻辑可能未做特殊保护,可能导致 Body 长度为负,进而报错或产生奇怪的视效。
  • 预期行为: 应该自动缩短淡入/淡出时长,或者强制正片最短时长限制。

回答:淡入淡出退化为0秒,并且给出警告日志(我希望对日志信息做警告和报错分级的日志收集单独显示)

Q4: 播放器跳帧/时间戳问题 (Invalid DTS/PTS) 是因为什么?

我的理解: 之前的实现使用了 concat -c copy(流复制),直接拼接了多个 Block 的原始数据包。 由于每个 Block 都是独立生成的,其内部的时间戳(PTS/DTS)在拼接处可能不连续,或者存在微小的重叠/空隙(由于帧率转换精度问题)。 现在的修复:

  1. 强制补帧 (tpad): 确保视频流不会因为精度问题短缺。
  2. 完全重编码 (libx264): 在拼接阶段重新生成所有帧和时间戳,强制保证连续性 (CFR)。

回答:所有的编码都应该在block内部完成,以避免重复编码造成性能浪费,后续的拼接只涉及

Q5: "还有其他画面也被跳过了" 可能是因为什么?

我的理解: 如果用户感觉到画面被跳过,除了播放器行为外,还可能是预处理时长判定错误。 如果预处理认为视频有 10秒,但生成的文件实际上只有 8秒。

  • Map 阶段下达命令:"截取第 8-10 秒作为 Tail"。
  • 但文件只有 8秒。FFmpeg 读不到数据,Tail 变成全黑或全背景(因 tpad 补帧可能是以最后一帧——即第8秒的帧——重复)。
  • 修正: 我已添加了 ffprobe 检测实际时长的逻辑,以消除这种"认知偏差"。

回答:这个实际单元调试时可以通过设置标志帧进行确认

3. 待确认事项

  1. 淡入/淡出曲线: 目前使用的是线性 (alpha=1 -> alpha=0)。是否需要对数或其他曲线?

    • (当前假设: 线性即可) 回答:暂定线性
  2. 背景模糊处理: 目前 Body 部分如果画面比例不一致,使用了模糊背景填充 (boxblur)。Tail 部分的背景使用的是即将在下一个Clip显示的全局背景,还是当前Clip的模糊背景

    • 纠正: 之前的代码逻辑中,Tail 部分的底层背景使用的是 Global Background (为了衔接下一个)。而 Body 部分的背景是 Blurred Clip 本身。
    • 视觉突变: 这意味着从 Body 到 Tail,背景会从 "模糊的当前视频" 突变/淡入到 "全局背景视频"。这是预期的吗?
    • (当前代码逻辑: Tail 叠加在 Global Background 上)。 回答:因为是渐变的,所以不影响,而且只有填充部分,影响不大

请针对以上内容进行批注或确认。一旦对齐,我将编写独立的测试脚本来验证每一个 Block 是否完美符合上述定义。

4. 测试与验证计划 (Test & Verification Plan)

为了统一共识并确保修复有效,我们将按照以下计划进行验证。不急于编码,先确保测试覆盖了所有关键场景。

核心目标

验证 Map (Block生成)Reduce (拼接) 两个阶段在不同输入源(B站/本地)和不同配置下的稳定性及正确性。

测试数据源 (从 LocalStorage 模拟)

我们将创建一个测试脚本 scripts/verify_pipeline.js,模拟从 LocalStorage 读取的卡片数据。

需覆盖的典型卡片类型:

  1. B站源卡片 (Bilibili Source)
    • 特点: 已下载并预处理(Preprocessed),通常在 temp 目录中。
    • 测试点: 预处理文件的路径处理、预处理元数据读取、不需要再次裁剪。
  2. 本地源卡片 (Local Source)
    • 特点: 原始文件路径,可能非标准帧率/分辨率。
    • 测试点: 自动标准化 (Encoding)、裁剪 (Clipping)、帧率转换。 补充:本地源预处理后也会将标准化后的文件放在temp中,所以和b站源一样

测试场景矩阵 (Test Scenarios)

ID 场景名称 输入源 淡入/淡出 转场 预期行为
CASE_01 标准全功能 B站/本地 2s / 2s 有转场音效 生成完整 Head(含背景+淡入)+Body+Tail。Check: 时长 = Clip+转场。
CASE_02 无转场纯淡入 B站/本地 1s / 1s Head 仅包含淡入部分。Head 背景时长 = 0。Check: 无空黑段。
CASE_03 超短视频回退 任意 2s / 2s 任意 Clip时长 < 4s。触发回退逻辑。输出 SimpleClip(无淡入淡出)。Check: 有警告日志
CASE_04 异构源视频 本地 1s / 1s 源视频为 60fps/24fps 或 VFR。Check: 输出 Block 严格为 30fps CFR

补充:淡入和淡出以及转场音频都是用户可选开启的,情况要考虑全面

验证标准 (Acceptance Criteria) (机器校验)

测试脚本将对生成的每一个 Block 和最终 Output 执行 ffprobe 校验:

  1. Block 级校验:

    • vcodec: h264 (libx264/nvenc)
    • acodec: aac
    • sample_rate: 48000
    • fps: 30 (必须恒定)
    • 关键: start_time ≈ 0.000000
  2. Pipeline 级校验:

    • 执行 ffmpeg -f concat -c copy ...
    • 必须无错误日志 (如 Invalid DTS, Non-monotonous DTS)。
    • 最终文件时长 = Sum(Block Durations)。

下一步计划

  1. 确认上述测试用例是否覆盖了您的疑虑。
  2. 编写 scripts/verify_pipeline.js
  3. 执行测试并生成报告。

补充:block级别的校验还要确定是否完成了时间统一编码