录后弹幕合成

先落盘保存纯净视频和 XML,再在回看工作台完成遮挡、弹幕压制和切片。

准备录像和弹幕

录后合成需要本地录像;压制弹幕还需要录制时保存的弹幕 XML,请在对应平台设置中开启「录制弹幕」。

如果只需本地保存,在直播间编辑页不选择投稿模板,并将后处理操作清空至「保留文件」。使用落盘下载器(默认 mesio),不要选择「边录边传」。需要保留纯净画面时,不在分段处理中配置弹幕压制或画面遮挡,并在配置覆写中关闭画面遮挡,留到回看工作台编辑。

在工作台编辑和合成

下播后打开 剪辑台 → 直播场次 → 回看,进入 画面与弹幕 面板。录后合成会生成独立的成品文件;素材是否保留仍取决于后处理和磁盘清理设置。

遮挡图层

  • 添加马赛克、模糊、纯色或 PNG/JPEG 图片图层。
  • 勾选「画面拖拽编辑」后,在播放器上拖动图层;八个控制柄可以缩放图片或区域。
  • 默认锁定图片宽高比,也可以取消锁定。左侧、顶部、宽度、高度和不透明度可以用数字精确调整。
  • 图层支持调整顺序。每个图层可以全程生效,也可以添加多个场次时间区间;区间终点不包含在效果内。
  • 图片以资源 ID 保存,配置和合成任务不会接受浏览器直接提交的服务器路径。

弹幕

打开「压制弹幕」后,工作台使用与导出相同的 ASS 生成流程预览。可以调整字体、字号、透明度、描边、滚动速度、显示范围、密度、场次偏移和分段偏移补偿。正偏移表示弹幕延后。

导出

「合成导出选段」会把精确剪辑、遮挡、图片叠加和弹幕压制放进同一次 FFmpeg 编码。多个源分段在同一个滤镜图中拼接,完成后得到可播放的 MP4;「导出整场 MP4」只在场次结束后可用。

第一次导出前,请在 空间配置 确认 FFmpeg 可用。打开回看中的「画面与弹幕」面板后,编码器检测会实际生成短视频,显示可用设备及失败原因。启用弹幕时还需要 libass 和随包中文字体。

弹幕 XML 到 ASS 的转换已经内置在 biliup 中:使用原生 Rust 实现,排版算法派生自 DanmakuFactory,随 biliup 本体和桌面版提供,不需要再安装 DanmakuFactory,也不再读取 danmaku_factory_path 配置(旧配置仍可正常加载,但该项会被忽略并在日志中提示)。预览和导出使用同一个转换流程和同一份字体:

  • CLI 发布包、桌面安装包和 Docker 镜像均随包提供 Noto Sans CJK SC 和 Noto Emoji,字体作为独立资源文件分发,不内嵌进可执行文件或 Python 扩展模块;
  • CLI 使用可执行文件旁的 render-tools/fonts/;Python 包仅提供基础 API,不携带字体或录制服务。字体文件不进仓库,构建与 CI 按固定 commit 与 SHA-256 取回(命令见 crates/biliup-cli/assets/render-tools/README.txt)。需要自定义字体时,在配置里把 render_fonts_dir 指向包含字体文件的目录。

工具页面的弹幕转换检测会真正启动一次转换来确认它可用。合成任务会显示排队、进度、失败和重试状态;取消或失败不会删除原视频、XML 或图片资源。

成品可以在任务列表中播放或下载,再交给你的切片整理和投稿流程。biliup 不会因为合成完成自动投稿;投稿仍由切片工作台和现有投稿流程决定。

时间和原片保护

视频、XML、遮挡区间和弹幕偏移使用同一场次时间轴。断流空档不会写入成品,空档中的弹幕会被丢弃;跨越空档的滚动弹幕会在边界结束。片段入点前仍在滚动的弹幕会保留必要的前导时间,避免切片开头重新出现。

合成任务使用创建时的配置和图片快照。之后修改工作台配置只影响新任务;原始视频、XML、工作台索引和旧成品仍可继续使用。

性能和故障处理

合成会重新编码视频,CPU 和临时磁盘占用会随分辨率、帧率和时长增加。默认「自动选择」优先使用通过真实编码探测的 GPU;没有可用硬件时使用 CPU,并在任务卡显示回退原因。长场次可以先导出需要的切片,再按需导出整场。

弹幕 XML 按流读取,导出的 ASS 逐行写入目标目录中的独占 .part 文件,写入完成后才发布正式文件;转换过程不创建独立临时目录,也不保存归一化 XML 中间文件。排序和避碰排版仍需保留弹幕数据,浏览器预览还需返回完整 ASS 文本。转换在 biliup 进程内完成,不启动外部程序;单次转换超过 120 秒或任务被取消时返回错误,取消、超时或写入失败都不会发布半成品 ASS。

缺失或损坏的 XML 会让启用弹幕的任务失败,并指出问题分段。关闭弹幕后可以重新导出;合法的空 XML 可以正常完成没有弹幕的成品。若任务失败,先检查 FFmpeg、编码器、字体、磁盘空间和源分段完整性,再使用任务列表中的「重试原配置」。

GPU 编码和使用情况

在回看「画面与弹幕 → 视频编码」中选择自动模式、CPU 或具体硬件编码器;多块 DRM GPU 可以选择设备节点。配置随场次保存,新任务冻结当时的配置。手动指定的编码器或设备无法使用时任务会失败,自动模式才会在开始合成前回退 CPU。同一任务的所有分段使用同一编码器,然后流复制拼接。

平台与显卡H.264 编码器运行条件
NVIDIA,Linux / Windowsh264_nvencFFmpeg 启用 NVENC,安装匹配的 NVIDIA 驱动;容器须透传 GPU 和驱动运行库
Intel,Linux / Windowsh264_qsvFFmpeg 启用 QSV / oneVPL,并同时安装调度库与 GPU 运行库(Arch Linux 为 libvpl、vpl-gpu-rt)
Intel / AMD,Linuxh264_vaapiFFmpeg 启用 VAAPI,安装驱动,进程可访问 /dev/dri/renderD*
AMD,Windows / 支持 AMF 的 Linux 驱动h264_amfFFmpeg 启用 AMF,安装对应 AMD 驱动和 AMF 运行库;Linux 可优先使用 VAAPI
任意平台,CPUlibx264FFmpeg 启用 libx264

列在 FFmpeg 的 -encoders 输出里并不代表驱动可用。界面会在检测后显示短编码结果,任务开始时也会按视频实际尺寸和帧率确认可用性。探测结果短暂缓存,避免多个页面同时反复启动 FFmpeg。修改工具或驱动后可点击「重新检查」;失败结果最多缓存 30 秒。

弹幕 ASS 仍由 libass 处理。使用 H.264 QSV 且视频尺寸为偶数、与目标尺寸一致,并且最多有 4 个持续生效的马赛克、平滑模糊或图片图层时,这些遮挡可走 QSV 滤镜路径;其他编码器或不满足条件的遮挡会回退到软件滤镜。GPU 编码和滤镜加速可减少相应处理环节的负担;解码、未加速滤镜、帧传输或磁盘吞吐仍可能限制整体速度。质量参数按各编码器采用约 20 的质量设置,不同编码器的画质和文件大小不保证完全相同。

侧栏 压制任务 显示所有场次的任务,支持按状态和场次 ID 筛选、分页、取消、重试、播放及下载;回看页面仍显示本场任务。任务卡每 2 秒刷新,包含实际编码器和设备、百分比、处理 FPS、倍速、耗时及预计剩余时间。倍速优先使用 FFmpeg 的媒体时间 / 实际时间;若硬件编码的时间戳为零,则按固定输出帧率和已编码帧数估算。准备、拼接、排队或缺少有效速度时不估算剩余时间。重试会创建新任务,保留原任务和原配置。

任务页的 GPU 卡分别显示整体利用率、视频编码引擎和显存。Linux 上 NVIDIA 读取 nvidia-smi(CSV / dmon,必要时回退 XML),AMD 读取驱动 sysfs,Intel 读取 intel_gpu_top。Windows 默认读取 WDDM 的 GPU Engine / GPU Adapter Memory 系统性能计数器,通过 DXGI 适配器 LUID 对应设备;NVIDIA 的 nvidia-smi 作为系统计数器不可用时的后备。Intel Video 引擎包含编码和解码活动。读不到的单项显示「不可用」,不会用整体占用代替编码占用,也不会把共享内存当独立显存。缺少工具、驱动不提供计数器或权限不足时仍可进行 GPU 编码,监测与编码可用性分别判断。Windows 的整体占用取最忙 GPU 引擎,编码占用单独取 Video Encode;计数器或显存预算不可读时对应指标显示不可用。

参考实现和参数说明:Jellyfin 硬件转码、Immich 硬件转码、NVIDIA FFmpeg 指南 和 FFmpeg 视频编码器。

Docker 中的 Intel VAAPI

Dockerfile 为 Linux/amd64 镜像安装 VAAPI DRM 运行库和完整 iHD 编码驱动 intel-media-va-driver-non-free,后者来自 Debian non-free 软件源,版权声明随镜像保留在 /usr/share/doc/;其他架构不安装该 Intel 驱动。此依赖补齐针对 VAAPI,不会额外安装 QSV / oneVPL 运行库。

使用核显时,在原有 Docker 启动参数中增加 --device /dev/dri:/dev/dri,并确保容器运行用户有权限访问目标 /dev/dri/renderD*。宿主机仍须有正常工作的 GPU 内核驱动;镜像内的库不能代替设备透传或修复宿主驱动。没有 GPU 时仍可使用 libx264,自动模式保留原有 CPU 回退。

Intel Video 引擎监测可能另外需要 --cap-add PERFMON,具体取决于内核及容器安全策略;该权限不是 VAAPI 编码的前提。不要为开启编码或读取监测数据默认使用 --privileged。监测与编码可用性应分别排查。

Intel QSV 检测失败

如果 VAAPI 正常但 QSV 报 Error creating a MFX session: -9,先检查 oneVPL GPU 运行库。仅安装 libvpl 调度库不足以运行 Intel GPU 编码;Arch Linux 还需要官方包 vpl-gpu-rt(与 intel-media-driver 版本匹配)。运行库补齐后重新检测,原先失败结果可能最多缓存 30 秒。不要把这个错误直接解释为显卡缺少编码能力。

接入分段后处理

自动打码和弹幕压制使用现有直播间 segment_processor,Noop 上传器也会执行它。 例如以下步骤把本段弹幕与遮挡同时压制,完成后才交给后续处理和上传:

segment_processor:
  - render:
      danmaku:
        enabled: true
      regions:
        - id: privacy
          effect_type: solid
          x: 0.1
          y: 0.1
          width: 0.2
          height: 0.1
          color: '#000000'

需同时开启弹幕录制。旧 mosaic_config 会自动转成同一个原生步骤,并与弹幕设置合并,避免两次编码。 上传后的 postprocessor 继续负责移动、删除等收尾操作。rm 会清理成品、弹幕,以及本次原生压制或转封装留下的原片和中间文件;设置了保留期或仍被剪辑引用时,整组文件延后清理。上传或投稿失败不会触发这项清理,显式设置空列表则保留文件。原生处理需要完整分段,不能用于边录边传。

文件关联从处理时开始记录,重启后仍有效。升级前已经遗留、没有关联记录的原片不会按文件名猜测删除;自定义 run 命令生成的额外文件仍由命令自行管理。

自动分段处理和工作台手动导出的成品统一保存到启动工作目录下的 renders/<场次ID>/。没有场次信息的自动处理保存到 renders/。普通自动处理使用 原文件名.processed.mp4;隔离原片完成遮挡后去掉 .unmasked 标记。上传、素材路径和索引会随成品路径更新。该目录目前是固定约定,没有单独的配置项。

直播间配置中的“处理流程”提供现有四类钩子的可视化编排:点击节点修改参数,在同一阶段内拖动或用上下按钮排序。“画面与弹幕”节点可以直接配置弹幕样式和遮挡区域。下载结束钩子在整场下载及分段处理完成后执行一次;配置该钩子后,普通录制会等待它完成再上传。保存仍使用 preprocessor、segment_processor、downloaded_processor、postprocessor 四个字段;清空上传后步骤表示保留文件,未配置该字段则沿用默认清理。

转换无需工作目录:XML 直接读入排版器,ASS 流式写入输出目录旁的单个 .ass 文件,转换完成后供 libass 按文件名读取;滤镜脚本使用 .ffgraph 文件,避免 Windows 命令行长度限制。两者处理结束后释放,不再生成分段视频、音频或拼接中间视频。最终视频写入同目录 .part 文件,写入并校验完成后发布为正常视频文件;.part 只表示尚未完成发布的输出。

下载结束脚本的输入和输出

downloaded_processor 在整场下载及分段处理完成后执行一次;配置该钩子时,普通录制会等待它完成再上传。边录边传在录制结束后执行,不能修改已提交的视频。空场次也会通知一次。未配置 postprocessor 时默认清理,显式空列表或仅配置脚本、回调时保留文件。

run 默认接收换行分隔的精确文件路径(视频及对应弹幕)。设置 json = true,或在处理流程中勾选 JSON 输入,改为接收与 v1.0.7 相同的六个字段:name、url、room_title、start_time、end_time、file_list。时间为秒级 Unix 时间戳,结束时间取实际录制结束时刻;JSON 的 file_list 只包含视频。

仅生成封面、通知或原地修改文件的脚本无需返回值。脚本若改名、转码、合并或拆分文件,需要在标准输出中单独打印一行 UTF-8 JSON,例如:

BILIUP_OUTPUT={"files":[{"video":"output.mp4","danmaku":"output.xml"}]}

相对路径以 biliup 的启动目录为基准,也可以返回绝对路径;结果行最大 1 MiB。

files 是该步骤完成后的完整产物列表,顺序也是上传顺序;每项必须有 video,danmaku 可省略或设为 null。每个脚本最多输出一条结果,其他 stdout/stderr 原样记入日志。后续脚本接收更新后的列表,上传也使用此列表。未返回结果则沿用原路径;返回无效、缺失、重复或未遮挡文件时停止本次上传并保留录像,不猜测新文件名、不扫描目录。脚本返回新路径时,程序不猜测它与原分段的时间映射,也不自动删除被替换的原片;如需继续在工作台使用原分段,脚本应保留这些文件。

下载器与保存后缀

在处理流程的「下载」设置中,选择支持转封装的下载器后才显示保存后缀。FFmpeg 可选 MP4、FLV、TS、MKV;ytarchive 可选 MP4、MKV。其他下载方式不显示后缀选项,切换到不支持的下载器时清空原后缀。跟随全局设置时按全局下载器判断能力;无法读取录制节点的继承配置时,需要显式选择下载器才能编辑后缀。Twitch 的普通 FFmpeg 选项实际通过 streamlink 录制,因此也不显示后缀选择。

FFmpeg 可以实际转封装。旧配置或 API 显式选择 mesio、stream-gears、streamlink 时,设置 MP4 等后缀不会把原始字节变成该容器:程序按实际容器保存。streamlink 从已录文件头识别容器,无法识别时保留临时录像。旧配置未显式选择下载器而要求 MP4 时,自动选择仍可使用 FFmpeg。

yt-dlp 直接在录像目录下载和合并,使用 after_move 返回的最终路径,包括合并、转封装后的后缀;archive 命中且没有报告产物时,不会拾取旧文件或封面、信息 JSON。下载失败时保留该目录中的未完成录像,不另建视频缓存目录或清理残留文件。ytarchive 的输出模板不带扩展名,默认输出 MP4,选择 MKV 时传入 --mkv。边录边传固定使用 Matroska。后续处理和投稿始终使用下载器实际报告的文件。