文章

LLM 视频输入管线生产实战:用帧采样、时间戳对齐与 Media Token 预算控制多模态成本

视频理解不只是多传几张图。本文从文件准入、解码探测、帧采样、时间戳对齐到 Media Token 预算,系统讲解如何建设可上线的 LLM 视频输入管线,并覆盖缓存、质量评估与常见误区。

背景:视频理解的难点不只是”多传几张图”

很多团队做完图片理解后,会自然地把视频理解看成”把视频拆成多张图片再问模型”。这个思路能跑通 Demo,但进入生产后会很快遇到三个问题。

LLM 视频输入管线生产实战:用帧采样、时间戳对齐与 Media Token 预算控制多模态成本

第一,成本不可控。 视频不是一张图,而是按时间展开的图像序列。一个 30 秒视频按 1 FPS 采样是 30 帧,按 5 FPS 采样就是 150 帧;如果每帧再使用高分辨率处理,media token、上传时间、编码时间和模型延迟都会明显上升。

第二,质量不可解释。 模型答错时,你需要知道它到底看到了哪些帧,哪些关键动作被跳过,时间戳有没有偏移,字幕和音频是否被纳入,长视频是否只取了前几分钟。如果只保存最终 prompt,很难复盘。

第三,工程链路更长。 视频输入涉及文件上传、转码、解码、抽帧、时间戳对齐、音轨处理、分段、缓存、模型调用和结果回灌。任何一层都可能失败——文件还在处理、视频损坏、某些帧无法解码、上传体积超限、时间戳丢失、模型只看到了缩略帧。

Gemini API 的视频理解文档明确说明,Gemini 可以处理视频,支持描述、分割、抽取信息、回答视频内容问题,并可以引用视频中的特定时间戳;它也区分 File API、Cloud Storage、Inline Data 和 YouTube URL 等输入方式。vLLM 的多模态输入文档也把视频解码后端、帧恢复、预抽帧序列和视频元数据作为单独问题处理。这说明视频输入已经不是简单附件,而是一条需要治理的数据管线

核心原理:视频输入要同时管理内容、时间和预算

视频文件不是模型输入的最终形态

模型最终看到的通常不是原始 MP4 文件本身,而是由平台或自建服务从视频中提取出的视觉表示:帧、patch、视觉 token、时间片段、音频片段或字幕。不同平台会隐藏一部分处理细节,但生产系统仍要在进入模型前掌握最关键的元数据:

type VideoInputProfile = {
  videoId: string;
  sourceUri: string;
  mimeType: "video/mp4" | "video/webm" | "video/quicktime";
  durationSec: number;
  fps: number;
  width: number;
  height: number;
  bytes: number;
  hasAudio: boolean;
  decodeProfile: string;
  samplingProfile: string;
  mediaResolution: "low" | "medium" | "high";
};

这份 profile 不是为了文档好看,而是为了让后续预算、缓存、回放和质量评估有共同基线。

Media Token 预算要按帧计算

Gemini 的 media resolution 文档说明,media_resolution 用来控制 Gemini API 处理图片、视频、PDF 等媒体输入时分配的最大 token 数,从而平衡质量、延迟和成本。文档还列出视频场景中不同分辨率的 token 近似口径:

分辨率每帧 token 估算适用场景
Low / Medium~70 tokens普通视频摘要、动作识别
High~280 tokens密集文本、小细节 OCR

这个信息对生产设计非常关键。它意味着视频预算可以抽象为:

media_tokens ≈ sampled_frames × tokens_per_frame(resolution)

如果一个 10 分钟视频按 1 FPS 采样,大约是 600 帧。即使每帧只按 70 token 估算,也已经是 42,000 media tokens,还不含文本 prompt、字幕、音频转写和模型输出。对于客服质检、课堂摘要、会议录像、监控片段分析等场景,如果不先做采样和预算,成本很容易失控。

时间戳是视频理解的第一公民

视频问题经常不是”画面里有什么”,而是”什么时候发生了什么”。例如:

  • 用户在哪一秒点击了错误按钮
  • 事故发生前 5 秒出现了什么
  • 讲师在第几分钟解释了某个概念
  • 监控画面中人物何时进入、何时离开
  • 商品视频里展示缺陷的是哪一段

因此,抽帧不能只保存图片,还要保存帧索引、原始 FPS、时间戳、采样策略和总帧数。vLLM 文档也指出,当客户端预先抽帧并发送 video/jpeg 序列时,可以通过 media_io_kwargs 传入 fpsframes_indicestotal_num_framesduration 等元数据,以保留原始视频的时间信息。

一个可回放的采样记录应类似这样:

{
  "video_id": "vid_20260709_001",
  "duration": 1800.0,
  "fps": 30.0,
  "total_num_frames": 54000,
  "sampling_profile": "scene_aware_32_frames_v2",
  "sampled_frames": [
    { "frame_index": 0, "timestamp": 0.0, "reason": "start" },
    { "frame_index": 4500, "timestamp": 150.0, "reason": "scene_change" },
    { "frame_index": 12600, "timestamp": 420.0, "reason": "query_relevant" }
  ]
}

没有这类记录,模型说”在 07:00 左右出现异常”时,平台很难判断它是真的看到了这一段,还是根据稀疏帧猜的。

工程落地:一条可上线的视频输入管线

1. 文件准入:先判断能不能进管线

视频进入模型前,先做文件准入,而不是直接上传。准入层至少检查:

  • MIME 类型和扩展名是否匹配
  • 文件大小是否超过 inline 上限
  • 时长是否超过任务允许范围
  • 分辨率和帧率是否异常
  • 是否有音轨、字幕轨
  • 是否可被解码
  • 是否包含过多黑屏、纯色帧或损坏片段

Gemini 文档建议:较大文件、较长视频、需要复用的视频使用 File API;小视频可以 inline;大于 20MB 总请求或显著时长的视频应使用 Files API。生产系统也应采用类似分流——短视频走同步或 inline,大视频走异步上传和处理状态轮询。

type VideoAdmissionDecision =
  | { action: "inline"; reason: string }
  | { action: "upload_file_api"; reason: string }
  | { action: "async_job"; reason: string }
  | { action: "reject"; reason: string };

function decideVideoAdmission(v: VideoInputProfile): VideoAdmissionDecision {
  if (v.bytes > 20 * 1024 * 1024 || v.durationSec > 60) {
    return { action: "upload_file_api", reason: "large_or_long_video" };
  }
  if (v.durationSec > 1800) {
    return { action: "async_job", reason: "long_video_requires_segmented_processing" };
  }
  if (!v.mimeType.startsWith("video/")) {
    return { action: "reject", reason: "unsupported_mime_type" };
  }
  return { action: "inline", reason: "small_one_off_video" };
}

准入决策要写入日志。否则同一类视频有时同步、有时异步,后续排障会很混乱。

2. 解码与探测:把 FFmpeg / OpenCV 变成可观测步骤

视频输入的第一类故障经常发生在解码阶段:容器格式可读但某些帧损坏,音轨异常,时长元数据不准,变帧率视频时间戳不连续。FFmpeg 文档提供了 stream selection、mapping 等基础能力,vLLM 文档则说明它的视频解码后端可以选择 OpenCV、PyAV 或 TorchCodec,而这些后端最终都依赖 FFmpeg。

生产系统应把解码探测作为显式步骤:

ffprobe -v error \
  -show_entries format=duration,size \
  -show_streams \
  -of json input.mp4

探测结果至少写入:duration、fps、width、height、codec、audio streams、subtitle streams、frame count estimate、probe error。不要等模型调用失败后才发现视频文件本身不可用。

3. 采样策略:不要所有视频都用固定 FPS

最简单的采样是固定 FPS,例如每秒取一帧。但这并不总是最优。

视频类型推荐策略说明
普通视频摘要均匀采样、低 FPS目标是覆盖整体内容
操作录像、监控、体育、教学演示场景切分 + 关键帧 + 问题相关采样依赖关键动作定位
屏幕录制、代码演示、表格、PPT 录屏关键帧 + 间隔采样,高分辨率需要读小字

近期视频理解研究也反复指出,长视频理解的难点在于从大量帧中选出和问题相关的关键片段。GenS 论文讨论了 long-form video 中大量帧带来的计算负担,并提出查询相关的 frame sampler;GroundVTS 也指出均匀采样可能造成关键帧稀疏和时间线索丢失。生产系统不一定要直接采用论文算法,但应避免把”固定每秒一帧”当成唯一策略。

一个实用的采样配置可以这样写:

video_sampling_profiles:
  summary_low_cost:
    strategy: uniform
    target_frames: 24
    media_resolution: low
    use_case: general_summary
  action_tracking:
    strategy: scene_aware
    target_frames: 64
    media_resolution: medium
    keep_timestamps: true
    use_case: event_localization
  screen_recording_ocr:
    strategy: keyframe_plus_interval
    target_frames: 40
    media_resolution: high
    keep_timestamps: true
    use_case: text_heavy_video

4. 时间戳对齐:采样帧必须能回到原视频

采样时不要只输出图片文件,还要输出 frame manifest:

type SampledFrame = {
  frameId: string;
  frameIndex: number;
  timestampSec: number;
  width: number;
  height: number;
  reason: "uniform" | "keyframe" | "scene_change" | "query_relevant";
  imageSha256: string;
};

模型回答中若引用”12:46 附近”,后端应该能定位到哪些输入帧覆盖了这一时间段,并把截图、原始帧、采样参数拿出来复盘。

对于预抽帧再上传的链路,尤其要保留 fpsframes_indicestotal_num_framesduration。否则模型看到的是一串图片,而不是有时间结构的视频。

5. Media Token 预算:先预算,再采样,再请求

视频请求应先估算预算,再决定采样策略:

type VideoBudget = {
  maxMediaTokens: number;
  maxFrames: number;
  resolution: "low" | "medium" | "high";
  allowAdaptiveSampling: boolean;
};

function estimateMediaTokens(frames: number, resolution: "low" | "medium" | "high") {
  const tokensPerFrame = resolution === "high" ? 280 : 70;
  return frames * tokensPerFrame;
}

function chooseFrameCount(durationSec: number, budget: VideoBudget) {
  const byBudget = Math.floor(budget.maxMediaTokens / estimateMediaTokens(1, budget.resolution));
  const byProfile = durationSec < 60 ? 24 : durationSec < 600 ? 48 : 96;
  return Math.min(byBudget, byProfile, budget.maxFrames);
}

这个估算不必追求和供应商账单完全一致,但要足够用于准入、告警和成本归因。真实 token 用量仍应以供应商返回的 usage 或平台账单为准。

6. 缓存:缓存文件,也缓存采样结果

视频缓存至少分三层:

缓存层内容说明
原始文件缓存同一 video sha256 不重复上传或处理避免重复传输和存储
探测缓存duration、fps、codec、streams 等元数据不用每次重新探测
采样缓存同一视频、同一 profile、同一分辨率下的帧序列复用已抽取的帧

缓存 key 应包含完整维度:

video_sample:{video_sha256}:{sampling_profile}:{resolution}:{decoder_version}:{preprocess_version}

不要只用 video_id,因为同一个视频在不同任务中可能使用不同采样策略和不同分辨率。

质量评估:视频管线要能回放,而不是只看最终答案

视频理解评估至少要覆盖三类任务:

  1. 整体摘要:模型是否覆盖主要场景、人物、动作、结论
  2. 时间定位:模型是否能回答”某个事件发生在什么时候”
  3. 细节识别:模型是否能读出屏幕文字、表格、UI 状态或小物体

每条评测样本应保存:原视频版本、采样 profile、抽帧 manifest、模型版本、media resolution、提示词版本和人工参考答案。

{
  "case_id": "video_eval_ui_bug_001",
  "video_sha256": "...",
  "task_type": "screen_recording_ocr",
  "expected_timestamps": [125.0, 138.5],
  "expected_observation": "用户点击保存后出现权限错误提示",
  "sampling_profile": "screen_recording_ocr_v2",
  "media_resolution": "high"
}

如果模型答错,应先判断:关键帧是否被采到?采到了但分辨率不够?时间戳对齐是否错误?还是模型理解失败?这比直接更换模型更有效。

适用场景

这套视频输入管线适合以下场景:

  • 客服质检、会议录像摘要、课堂视频问答
  • 操作录屏分析、UI 测试、Computer-use 回放
  • 安防片段、事故视频、质检视频的事件定位
  • 商品视频、培训视频、直播切片的内容理解
  • 长视频离线分析与批处理任务

不适合直接套用的场景包括:强实时视频通话、医疗影像诊断、法律证据原件分析、工业缺陷检测。这些场景需要更严格的模型评估、人工复核和合规链路。

常见误区

误区一:帧越多越好

帧越多,成本和延迟越高,但质量不一定线性提升。对普通摘要,少量代表帧可能足够;对事件定位,关键是采到关键时刻,而不是平均塞入更多帧。

误区二:只保存抽出的图片,不保存时间戳

没有时间戳,视频就退化成图片集合。模型输出无法回到原视频,质量评估也无法判断关键事件是否被采到。

误区三:所有视频都用高分辨率

高分辨率适合读小字、UI、表格和细节;普通动作识别和摘要通常低/中分辨率即可。Gemini 文档也建议一般视频使用 low 或 medium,高分辨率主要用于 text-heavy 或小细节场景。

误区四:把解码失败当成模型失败

很多视频问题发生在模型之前:文件损坏、帧读取失败、变帧率时间戳异常、音轨缺失。解码和采样步骤必须有独立错误码。

误区五:长视频全部一次性送入模型

长视频更适合分段处理:先按章节、场景或时间窗口切分,再做局部分析,最后做汇总。一次性塞入过多帧既贵,也难以解释。

上线检查清单

文件准入:

  • 是否限制文件大小、时长、分辨率、帧率和 MIME 类型
  • 是否区分 inline、File API、Cloud Storage、异步任务
  • 是否记录原始文件 hash 和来源
  • 是否处理文件仍在 processing 或 failed 的状态

解码与采样:

  • 是否用 ffprobe 或等价工具记录 duration、fps、codec、streams
  • 是否有统一采样 profile
  • 是否保存 frameIndex、timestamp、reason、image hash
  • 是否支持损坏帧跳过、恢复或失败标记

预算与成本:

  • 是否按采样帧数和 media resolution 估算 media tokens
  • 是否按任务类型限制 max frames
  • 是否记录实际 usage、延迟和成本
  • 是否支持超预算时自适应降低采样密度

质量与回放:

  • 是否有视频黄金样本集
  • 是否覆盖摘要、事件定位、细节识别三类任务
  • 是否能回放同一视频、同一采样策略、同一模型版本
  • 是否区分采样失败、解码失败、模型理解失败

结论

LLM 视频理解不是把 MP4 传给模型这么简单。真正的生产问题在于:哪些视频能进来,抽哪些帧,按什么时间轴对齐,花多少 media token,失败后怎么回放,质量怎么评估。

建议团队先把视频输入管线拆成五个可观测步骤:文件准入 → 解码探测 → 帧采样 → 时间戳对齐 → 预算检查。只要这五步稳定,后续无论接 Gemini、OpenAI 图片帧方案、自建 vLLM 多模态模型,还是离线批处理,都能有清晰的成本边界和排障路径。

参考资料

常见问题

视频输入为什么不能直接把原始文件丢给模型?
部分平台支持直接上传,但生产系统仍需治理文件大小、处理状态、采样策略、时间戳和预算。否则模型答错时无法判断它看到了哪些帧,也无法解释成本为何升高。
帧采样应该用固定 FPS 还是关键帧?
普通摘要先从低 FPS 或均匀采样开始;动作定位、监控、屏幕录制更适合关键帧、场景切分或问题相关采样。最终应通过黄金样本集比较质量、延迟和成本。
为什么时间戳对齐这么重要?
视频理解不只看单帧内容,还要回答某个动作何时发生、前后关系是什么。没有采样帧索引、原始 FPS 和时间戳,模型输出难以回放、纠错和审计。
采样结果可以缓存多久?
只要原视频 hash、采样 profile、解码器版本、预处理版本和 media resolution 都不变,采样结果可以长期复用。任一项变化都应生成新缓存 key。