首页
/ Docling 音视频处理实战:ASR 流水线、视频抽帧、说话人分离与 Whisper 后端选择

Docling 音视频处理实战:ASR 流水线、视频抽帧、说话人分离与 Whisper 后端选择

2026-09-06 14:02:13作者:凌朦慧Richard

本文基于 Docling 官方文档《Processing audio and video》(docs/usage/processing_audio_media.md)并结合仓库源码展开,讲解如何将音频与视频文件转换为结构化 DoclingDocument:包括 AsrPipelineVideoPipeline 的接入方式、帧采样模式、说话人分离、Whisper 三种推理后端(Native / MLX / WhisperS2T)的自动选择与手动强制机制、CLI 参数以及已知限制。读完后你可以直接在自己的工程中接入 Docling 的语音转写能力,构建可检索的音视频知识库或 RAG 流水线。

总览:音频与视频走两条不同的流水线

Docling 将音频和视频文件转换为与 PDF、DOCX 等格式完全一致的中间表示 DoclingDocument,之后可以导出为 Markdown、JSON、HTML 或 DocTags,并直接接入 RAG 流水线、摘要器或搜索索引。

从源码结构看,音频与视频分别由两条流水线处理:

  • 音频文件ASR 流水线AsrPipeline),只做语音转写;
  • 视频文件走专用的视频流水线VideoPipeline),在转写音轨之外,还会抽取有代表性的帧,并可选地做说话人分离(diarization)

两条流水线的转写内核都是 OpenAI Whisper。默认情况下会根据硬件自动选择最合适的后端——Apple Silicon 上选 mlx-whisper,其他平台选原生 Whisper——因此基础用法不需要任何硬件相关配置。要更换模型规模、强制指定后端、或使用实验性的高吞吐 WhisperS2T 后端,参见后文选择 ASR 模型与后端一节。

在实现层面,AsrPipelineVideoPipeline 都通过同一个工厂 _AsrModelFactory.create(...) 构建转写器(见 asr_transcriber.py),根据 asr_options 的具体类型实例化 _NativeWhisperModel_MlxWhisperModel_WhisperS2TModel 三者之一。

支持的格式与 ffmpeg 依赖

类型 格式
音频 WAV、MP3、M4A、AAC、OGG、FLAC
视频 MP4、AVI、MOV、MKV、WEBM

视频文件会自动抽取音轨再转写,无需手动运行 FFmpeg。上述格式列表与源码中的 MIME 映射一致:音频后缀映射见 asr_transcriber.py,视频后缀映射见 video_pipeline.py

注意:必须安装 ffmpeg。 Whisper 的音频解码依赖 ffmpeg 可执行文件在 PATH 中可用。这适用于 MP3、WAV、M4A、AAC、OGG、FLAC 等常见音频格式,也适用于视频(其音轨与帧都是先抽取再处理的)。请通过系统包管理器安装——例如 macOS 上 brew install ffmpeg,Debian 系 Linux 上 apt-get install ffmpeg,Windows 上 winget install ffmpeg

源码中两处都会显式检查:ASR 侧在 asr_transcriber.py 中调用 shutil.which("ffmpeg"),缺失时把 ConversionStatus 置为 FAILURE 并写入错误项;视频侧在 video_pipeline.py 做同样检查。

安装

ASR 流水线是可选附加项(optional extra),与基础包一起安装:

pip install "docling[asr]"

或使用 uv

uv add "docling[asr]"

视频场景(含可选的说话人分离)改装 format-video 附加项:

pip install "docling-slim[format-video]"

format-video 已经包含 asr 提供的一切(转写),外加帧采样与说话人分离所需的依赖(resemblyzersoundfilescikit-learnlibrosa)。

注意:Linux + CUDA 下的 WhisperS2T。 可选的 WhisperS2T 后端使用 CTranslate2,运行时会加载 NVIDIA 的 cuBLAS 共享库。在 Linux 上,如果 WhisperS2T 模型加载因找不到该库而失败,需要把它加入 LD_LIBRARY_PATH。当 cuBLAS 通过 pip wheel(如 nvidia-cublas-cu12)安装时,共享库位于环境 site-packages 下的 nvidia/cublas/lib 目录。

基础用法:AsrPipeline 转写音频

from pathlib import Path

from docling.datamodel import asr_model_specs
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import AsrPipelineOptions
from docling.document_converter import AudioFormatOption, DocumentConverter
from docling.pipeline.asr_pipeline import AsrPipeline

pipeline_options = AsrPipelineOptions()
pipeline_options.asr_options = asr_model_specs.WHISPER_TURBO

converter = DocumentConverter(
    format_options={
        InputFormat.AUDIO: AudioFormatOption(
            pipeline_cls=AsrPipeline,
            pipeline_options=pipeline_options,
        )
    }
)

result = converter.convert(Path("recording.mp3"))
doc = result.document

# 导出为 Markdown
print(doc.export_to_markdown())

这个例子使用的是「纯」AudioFormatOption/AsrPipeline,只做转写——它对任何 docling 能从中提取音频的文件都有效,视频文件也一样。但视频文件通常应使用专用的视频流水线(它还会抽帧、可归因说话人),见处理视频一节。

关于 AsrPipelineOptions 有两个值得注意的源码细节:

  • asr_options 字段默认值是 WHISPER_TINY 自动选择预设(见 pipeline_options.py),即不指定时跑最小的 tiny 模型;示例中显式换成 WHISPER_TURBO 是推荐做法。
  • AsrPipeline 仅接受 NoOpBackend 作为后端(asr_pipeline.py 中的 is_backend_supported),也就是说它不经过常规文档解析后端,直接操作原始音频文件路径或字节流。

导出为不同格式

result.document 是一个 DoclingDocument,可以导出到任何受支持的格式:

doc.export_to_markdown()   # Markdown
doc.export_to_dict()       # 可 JSON 序列化的 dict
doc.export_to_html()       # HTML
doc.export_to_doctags()    # DocTags

更多导出选项参见序列化文档

理解输出:带时间戳的段落级 Markdown

ASR 流水线产出段落级 Markdown,每个片段(segment)带时间戳:

[time: 0.0-4.0]  Shakespeare on Scenery by Oscar Wilde

[time: 5.28-9.96]  This is a LibriVox recording. All LibriVox recordings are in the public domain.

这种结构化输出可以直接作为向量嵌入模型、摘要器或其他下游环节的输入。

从源码看,[time: x-y] 前缀由 _ConversationItem.to_string() 生成(asr_transcriber.py);每个片段写入文档时会附带 TrackSource(start_time, end_time, voice=speaker)(见 asr_transcriber.py),也就是说时间戳与说话人信息是保留在 DoclingDocument 结构内的,而不只是文本表面。此外,对 end_time <= start_time 的零时长片段,实现会自动加 0.001s 的 epsilon 修正以避免校验失败(asr_transcriber.py)。

实战用例:可搜索的会议录音库

工程团队的一个常见问题:每次全员大会、客户通话、设计评审都会录音,录音堆积在网盘或 S3 上,没人回看、没人能搜索,组织知识被锁死在音频文件里。Docling 解决的是摄取(ingestion)这一步;再配一个向量库,就拥有了覆盖整个音频档案的可查询知识库。

独立转写脚本

核心逻辑约 30 行:

from pathlib import Path

from docling.datamodel import asr_model_specs
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import AsrPipelineOptions
from docling.document_converter import AudioFormatOption, DocumentConverter
from docling.pipeline.asr_pipeline import AsrPipeline


def main():
    audio_path = Path("videoplayback.mp3")

    pipeline_options = AsrPipelineOptions()
    pipeline_options.asr_options = asr_model_specs.WHISPER_TURBO

    converter = DocumentConverter(
        format_options={
            InputFormat.AUDIO: AudioFormatOption(
                pipeline_cls=AsrPipeline,
                pipeline_options=pipeline_options,
            )
        }
    )

    result = converter.convert(audio_path)
    md = result.document.export_to_markdown()
    Path("transcript.md").write_text(md)
    print(md)


if __name__ == "__main__":
    main()

仓库内也提供了可参考的完整示例脚本 minimal_asr_pipeline.py

用 LangChain 搭建 RAG 流水线

Docling 通过 DoclingLoader 与 LangChain 集成,它封装了 DocumentConverter 并自动处理分块(chunking)。构建覆盖音频档案的检索流水线:

from langchain_docling import DoclingLoader
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

# 加载并分块目录下的所有音频文件
loader = DoclingLoader("recordings/")
docs = loader.load()

# 嵌入并建索引
vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings())
retriever = vectorstore.as_retriever()

# 自然语言查询
results = retriever.invoke("What did we decide about the auth service in Q3?")

DoclingLoader 的更多选项参见 LangChain 集成指南,LlamaIndex 侧可参见 LlamaIndex 集成

处理视频:VideoPipeline

视频文件路由到专用的视频流水线VideoPipeline)。它像上面的 ASR 流水线一样转写音轨,并且额外:

  • 从视频中抽取代表性帧,以 picture 项形式嵌入输出的 DoclingDocument
  • 可选地通过说话人分离为转写片段标注说话人

安装 format-video 附加项(见安装)即可获得帧采样与说话人分离支持:

from pathlib import Path

from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import VideoPipelineOptions
from docling.document_converter import DocumentConverter, VideoFormatOption
from docling.utils.video_frame_sampling import VideoFrameSamplingMode

pipeline_options = VideoPipelineOptions(
    frame_sampling_mode=VideoFrameSamplingMode.SCENE_CHANGE,
    scene_change_prominence=0.03,  # 会议场景推荐值
    enable_diarization=True,       # 需要 resemblyzer,见安装说明
)

converter = DocumentConverter(
    format_options={
        InputFormat.VIDEO: VideoFormatOption(pipeline_options=pipeline_options)
    }
)

result = converter.convert(Path("meeting.mp4"))
doc = result.document

print(doc.export_to_markdown())

VideoPipeline 的实际执行流程与文档描述一一对应,见 video_pipeline.py_process_video

  1. 解析输入:支持本地路径与 BytesIO 字节流(后者落盘为临时文件);
  2. ffmpeg 抽音轨:以 16kHz 单声道 pcm_s16le WAV 抽取(_extract_audio),随后交给共享的 ASR 转写器;
  3. 句子合并:转写片段先经 _merge_into_sentences 按句末标点(. ? !)合并成句子块,时间范围取所有贡献片段的并集(asr_transcriber.py)——这让「一个块对应一个说话人」的归因更干净;
  4. 说话人分离(若 enable_diarization=True):趁 WAV 还存在时调用 diarize(...) 聚类,再 assign_speakers(...) 把说话人写入每个片段;
  5. 帧采样:按 frame_sampling_mode 选择 FixedIntervalFrameSamplerSimpleSceneChangeFrameSampler
  6. 按时间戳合并:把转写项与帧统一排成事件列表,按 (timestamp, 类型) 排序后依次写入文档——文本用 add_text(..., source=TrackSource(start_time, end_time, voice=speaker)),帧用 add_picture(..., source=TrackSource(start_time, ...)),最终在 Markdown 中形成「字幕与画面按时间交错」的结构。

帧采样模式

VideoPipelineOptions.frame_sampling_mode 控制代表性帧的选择方式:

模式 选项值 行为
固定间隔 VideoFrameSamplingMode.FIXED_INTERVAL(默认) frame_interval_seconds(默认 10s)取一帧。
场景切换 VideoFrameSamplingMode.SCENE_CHANGE 每个检测到的场景取一帧,并按清晰度择优。默认情况下灵敏度按视频自动标定。

各场景的推荐配置(与 VideoPipelineOptions 的文档字符串一致,见 pipeline_options.py):

使用场景 配置
商务会议 frame_sampling_mode=SCENE_CHANGE, scene_change_prominence=0.03
讲座录像 frame_sampling_mode=SCENE_CHANGE, cuts_per_minute=2.0
一般视频 frame_sampling_mode=FIXED_INTERVAL, frame_interval_seconds=10.0

max_sampled_frames 无上限值地封顶两种模式下的总采样帧数;设 generate_frame_images=False 可完全跳过帧采样、只做转写。VideoPipelineOptions 中与场景检测相关的完整参数及默认值如下(均定义于 pipeline_options.py):

参数 默认值 说明
frame_sampling_mode FIXED_INTERVAL 帧选择策略
frame_interval_seconds 10.0 固定间隔模式的间隔秒数(必须 > 0)
scene_change_prominence None(自动标定) 场景峰检测的显著度阈值;None 表示按视频运动自动标定
scene_change_probe_fps 1.0 场景探测用的低帧率
min_scene_duration_seconds 2.0 接受新场景前的最小时长
scene_change_smooth_window 2 峰值检测的平滑窗口(帧数),越大越平滑
max_sampled_frames None 采样帧数上限
generate_frame_images True 是否把代表性帧以 picture 项嵌入输出文档
enable_diarization False 是否启用说话人分离
asr_options WHISPER_TINY(自动选择预设) 音轨转写使用的 ASR 模型

帧采样的具体实现位于 video_frame_sampling.py

说话人分离

enable_diarization=True 即可把转写片段归因到说话人。分离通过 Resemblyzer 嵌入聚类完成,并自动检测说话人数量;它需要 format-video 捆绑的额外依赖(resemblyzersoundfilescikit-learnlibrosa)。如果这些依赖未安装,分离会被静默跳过并记录一条警告——转写与帧采样照常进行。这一容错行为在源码中可以直接验证:video_pipeline.pydiarize/assign_speakers 的调用包裹在 try/except 中,失败仅 _log.warning("Speaker diarization failed: ...")。分离实现位于 speaker_diarization.py

命令行方式

# 固定间隔采样(默认)
docling --to md video.mp4

# 场景切换采样,按会议场景调参
docling --to md --video-sampling-mode scene --video-prominence 0.03 video.mp4

# 场景切换采样 + 说话人分离
docling --to md --video-sampling-mode scene --video-diarization video.mp4
标志 默认 说明
--video-sampling-mode fixed fixedscene
--video-frame-interval 10.0 固定间隔模式下相邻帧的秒数。
--video-cuts-per-minute 0.0(未设置) 目标每分钟场景切换数;设置后会覆盖 --video-prominence
--video-prominence 0.0(自动) 场景切换灵敏度阈值。0 表示按视频运动自动标定。
--video-diarization 关闭 启用说话人分离。需要 resemblyzer

完整标志列表见 CLI 参考。CLI 侧的参数落地逻辑在 cli/main.py:视频输入构建 VideoPipelineOptions 时,scene 模式下若同时给出 --video-cuts-per-minute--video-prominence,以前者为准,否则 prominence 为 0 时走自动标定路径。

选择 ASR 模型与后端

Docling 内置三个可互换的 ASR 后端,都由上面的 asr 附加项安装:

后端 硬件 说明
Native Whisper openai-whisper(PyTorch) CPU、CUDA 默认;兼容性最广
MLX Whisper mlx-whisper Apple Silicon(MPS) 面向 M 系列 Mac 优化
WhisperS2T whisper-s2t-reborn(CTranslate2) CPU、CUDA 可选且实验性;批式解码,高吞吐

自动后端选择

自动选择预设——WHISPER_TINYWHISPER_BASEWHISPER_SMALLWHISPER_MEDIUMWHISPER_LARGEWHISPER_TURBO——会根据检测到的硬件替你选后端,优先级如下:

  1. MLX Whisper —— Apple Silicon 上且已安装 mlx-whisper 时;
  2. Native Whisper —— 其他所有硬件。

WhisperS2T 永远不会被自动选择,必须显式选择(见下)。

这一机制的实现在 asr_model_specs.py_detect_hardware_and_libraries() 先用 torch.backends.mps.is_built() and torch.backends.mps.is_available() 判断 Apple Silicon,再尝试 import mlx_whisper。每个 WHISPER_* 预设(如 _get_whisper_turbo_model())在模块导入时执行该检测,命中 MPS 则返回 InlineAsrMlxWhisperOptions(对应 mlx-community/whisper-*-mlx 仓库,其中 medium/large 用 8bit 量化版本),否则返回 InlineAsrNativeWhisperOptions

这也是为什么基础用法示例无需硬件相关代码——asr_model_specs.WHISPER_TURBO 在 Mac 上跑 MLX,在 Linux/Windows 上跑原生 Whisper。WHISPER_TURBO 是不错的默认选择;要换模型规模,换成其他自动选择预设即可:

from docling.datamodel import asr_model_specs

pipeline_options.asr_options = asr_model_specs.WHISPER_LARGE

强制指定后端

每个规模都有绕过硬件检测的显式变体,后缀为 _NATIVE_MLX_S2T,用于在任意平台上锁定后端:

from docling.datamodel import asr_model_specs

# Native OpenAI Whisper(CPU / CUDA)
pipeline_options.asr_options = asr_model_specs.WHISPER_TURBO_NATIVE

# MLX(Apple Silicon)
pipeline_options.asr_options = asr_model_specs.WHISPER_TURBO_MLX

其余设置——基础用法中的 DocumentConverter——保持不变。

除上述变体外,asr_model_specs.py 还提供了英文专用与 Distil-Whisper 的 Native 预设,如 WHISPER_TINY_EN_NATIVEWHISPER_BASE_EN_NATIVEWHISPER_DISTIL_LARGE_V3_NATIVEWHISPER_DISTIL_LARGE_V3_5_NATIVE。其中 Distil 系列不在 openai-whisper 的模型注册表里,源码通过 _DISTIL_WHISPER_OPENAI_CHECKPOINTS 映射从 Hugging Face 仓库下载 OpenAI 格式 checkpoint 再交给 whisper.load_model 加载——这是一个值得了解的实现细节:它们能工作是因为 HF 仓库发布了原始 OpenAI 格式权重。

WhisperS2T:高吞吐转写

WhisperS2T 通过 CTranslate2 运行 Whisper,采用批式、VAD 分段的解码。在 CPU 与 CUDA 上它通常是速度最快的后端,且在较大模型规模下比原生 Whisper 占用更少显存,适合大批量文件转写。它是实验性且 opt-in 的——选择一个 _S2T 预设即可:

from docling.datamodel import asr_model_specs

pipeline_options.asr_options = asr_model_specs.WHISPER_LARGE_V3_S2T

可用的 _S2T 预设:

预设 HuggingFace 模型 多语言?
WHISPER_TINY_S2T tiny
WHISPER_TINY_EN_S2T tiny.en 仅英文
WHISPER_BASE_S2T base
WHISPER_BASE_EN_S2T base.en 仅英文
WHISPER_SMALL_S2T small
WHISPER_SMALL_EN_S2T small.en 仅英文
WHISPER_DISTIL_SMALL_EN_S2T distil-small.en 仅英文
WHISPER_MEDIUM_S2T medium
WHISPER_MEDIUM_EN_S2T medium.en 仅英文
WHISPER_DISTIL_MEDIUM_EN_S2T distil-medium.en 仅英文
WHISPER_LARGE_V3_S2T large-v3
WHISPER_DISTIL_LARGE_V3_S2T distil-large-v3 仅英文
WHISPER_DISTIL_LARGE_V3_5_S2T distil-large-v3.5 仅英文
WHISPER_LARGE_V3_TURBO_S2T large-v3-turbo 是(不支持 translate

英文专用预设会拒绝非 en 语言与 translate 任务;large-v3-turbo 虽为多语言但不支持 translate。多语言转写或语音翻译请使用多语言预设(如 WHISPER_LARGE_V3_S2T)。这个「拒绝」不是文档约定,而是真实的校验逻辑:pipeline_options_asr_model.py 中维护了 _ENGLISH_ONLY_S2T_REPOS_NO_TRANSLATE_S2T_REPOS 两个集合,InlineAsrWhisperS2TOptionsmodel_validator同文件 L432-L449)在构造选项时直接抛 ValueError

要调优吞吐与精度,可以不用预设、直接构造选项对象:

from docling.datamodel.pipeline_options_asr_model import (
    InferenceAsrFramework,
    InlineAsrWhisperS2TOptions,
)

pipeline_options.asr_options = InlineAsrWhisperS2TOptions(
    repo_id="large-v3",
    inference_framework=InferenceAsrFramework.WHISPER_S2T,
    language="en",
    torch_dtype="float16",  # float32 | float16 | bfloat16
    batch_size=8,           # 越大吞吐越高、显存占用越大
    beam_size=1,            # 1 = 贪心解码(最快);更大可能提升精度
)

InlineAsrWhisperS2TOptions 的完整字段及默认值(见 pipeline_options_asr_model.py):language(默认 "en")、task(默认 "transcribe")、torch_dtype(默认 "float16"bfloat16 需要 compute capability ≥ 8.6)、batch_size(默认 8)、beam_size(默认 1)、word_timestamps(默认 False,开启需额外对齐模型并增加耗时)、num_threads(CPU 推理线程数)、initial_prompt(可为转写风格/领域词汇提供上下文提示)。

源码中还有两个对运行环境很有用的保护逻辑(asr_transcriber.py):

  • CPU 上自动降精度:CTranslate2 不支持 CPU 的 float16/bfloat16 推理,_WhisperS2TModel 检测到 device == "cpu"compute_typefloat16/bfloat16 时,会告警并回退到 float32,避免显式的 *_S2T 预设(为 CUDA 性能默认 float16)在纯 CPU 环境加载失败;
  • 大模型 mel 维数large-v3distil-large-v3distil-large-v3.5large-v3-turbo 会自动附加 n_mels=128 再加载。

注意:WhisperS2T 在 Apple Silicon 上不可用。 whisper-s2t-reborn 依赖仅安装在非 Apple Silicon 平台上,因此 M 系列 Mac 无法使用 _S2T 预设——请改用 native 或 MLX 后端。Linux + CUDA 环境如果模型加载失败,参见安装中的 cuBLAS 说明。

命令行方式

docling CLI 通过 --asr-model 选择任意预设(取值为小写预设名)。音频输入自动路由到 ASR 流水线、视频输入路由到视频流水线(依据文件扩展名),无需额外标志来选流水线——--asr-model 同时控制两者的转写后端:

# 自动选择默认
docling --to md --asr-model whisper_turbo recording.mp3

# 强制 native Whisper
docling --to md --asr-model whisper_turbo_native recording.mp3

# WhisperS2T,蒸馏版 large-v3
docling --to md --asr-model whisper_distil_large_v3_s2t recording.mp3

CLI 内部通过 _resolve_asr_options(asr_model)cli/main.py)把 AsrModelType 枚举映射到对应的选项对象;--asr-model 的全部合法取值即 asr_model_specs.pyAsrModelType 枚举定义的 34 个小写名称(6 个自动选择、6 个 _mlx、13 个 _native、14 个 _s2t)。完整列表见 CLI 参考

已知限制

限制 变通方案
不输出 SRT 字幕 可通过 doc.save_as_vtt(...) 输出 WebVTT。要 SRT 请使用 openai-whisper 命令行:whisper audio.mp3 --output_format srt
纯音频 ASR 流水线没有说话人分离 视频侧可通过 VideoPipelineOptions.enable_diarization 使用分离——见处理视频。纯音频分离建议把 pyannote-audio 作为前置或后置处理步骤
无词级时间戳 当前导出格式不支持

对知识检索类用例(RAG、搜索、摘要),段落级 Markdown 通常就够用;上述限制主要影响字幕生成类工作流。

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐