1KeyTranscoder v0.8.0 — 音频输出接入生产管线 · 格式感知 · 外挂音频
v0.8.0 把 v0.7.1 建立的 PCM 音频管线真正接进生产输出,并补上三件此前
只靠"调用方自觉"的事:输入格式判定、输出编码继承、外挂音频。
全部仍然是音频域内部的能力,视频编码后端与元数据保留管线一行未改。基线
v0.7.1(tagv0.7.1=e613b07)。
详细设计与实测证据见仓库
docs/release_notes_v0.8.0.md与
docs/design/architecture.md§6.2–§6.6。
Included
- Audio format-aware alignment — 输入是 PCM 还是 compressed 由 ffprobe 的
codec_name唯一判定(pcm_*= uncompressed,其余 = compressed):
PCM 输入默认允许进入 alignment,compressed 输入默认原样保留。
alignment: disabled可显式关闭。reference 仍然从不自动猜 —— 必须由
sync.reference显式给出,否则"允许对齐"不会产生任何 offset。 - Compressed audio re-encode for alignment — 显式要求 compressed 输入对齐时
输出 §5 的 warning,并走 decode → PCM → align → 重编码,输出 codec 保持
来源 codec。实测:注入 480 样本延迟的 Opus 目标估计出 480、AAC 目标
1504(480 + AAC 解码 priming 1024),两者对齐后互相关残差 0 —— 这正
是"禁止在压缩包上伪造时间戳平移"的量化理由。 - External audio discovery — 同一目录下按确定性文件名规则发现外挂录音:
音频主干必须以视频主干精确开头,且其后的第一个字符必须是结束 /-/_;
只扫描视频所在目录与音频扩展名白名单。排序完全确定(精确主干 → 数字序号 →
其它后缀),数字段按数值比较(clip001_2在clip001_10之前),前导零
归一化但仍是不同候选,同分用完整文件名 lexical order 收尾。多个候选全部
纳入,没有候选不报错。 - External audio mapping — 外挂音频严格遵循最终 mapping:
source(跟随每个
输入的流结构,默认)/independent(一声道一条流)/grouped(n)(连续 n 声道
一条流,跨文件继续成组)。4CH + grouped(2)→ 2×stereo,8CH + grouped(2)
→ 4×stereo,3CH + grouped(2)→ 2CH + 1CH(奇数剩余保留,不丢弃也不复制)。
不变量:Σ 输出声道 == 有效映射声道,否则报错而不是静默丢声道。 - AAC / Opus manual bitrate — 输出编码优先级链
manual override > source-derived defaults > encoder default:PCM 输入默认
PCM 输出,compressed 输入保持来源 codec + 来源 bitrate,手动format/
bitrate覆盖之(AAC 128k/192k/256k、Opus 64k/96k/128k …)。lossless 输出带
bitrate明确拒绝,不静默忽略。codec 继承按输出流进行,因此一条 AAC 外挂
录音与一条 PCM 源可以在同一份输出里各自保持自己的 codec。 - Mapping preservation — alignment 与重编码只移动样本时间轴,绝不改变声道
身份、流顺序或声道集合;原视频音频的输出结构由既有输出单元决定,不被 mapping
策略改写。整条完整源流仍然-map+ stream copy(不解码、不重编码),只有必须
重建的流才渲染 + 编码,两者可在同一次输出里混用。 - Regression coverage —
--level unit590 PASS / 0 FAIL,
--level full856 PASS / 0 FAIL(unit 590 + toolchain 16 + full 250)。
相对 Phase 4C 基线(unit 511 / full 728)新增 128 条断言、0 条 FAIL、
0 条被删除。
Fixed
- alignment 与 stream copy 不能混用:
-map没有"时间原点"可言,而有 offset 的
声道是按统一 timeline 重新取样的(window 起点可能不是 0)。两者混在一个容器里
就是两条流各有各的原点 —— 表现出来恰好是"对齐没生效"。现在一旦产生非零
offset,整份输出共用一份 render window,每条输出流都由 render 产出,组数与
每组声道保持不变。 - AAC 容器由裸 ADTS 改为 MP4 家族(
.m4a):实测PCM → AAC(ADTS) → 解码
会让内容整体后移 1024 个样本(编码器 priming,ADTS 无法携带),"为重编码对齐"
会被这一步吃掉;MP4 容器把 priming 写进 edit list,实测位置一致。
同时_probe_audio()不再把nb_frames当样本数 —— MP4 家族的音频流报的是
packet 数(AAC 一包 1024 样本)。
CLI
- 没有新增参数:全部能力通过既有的
--audio-planJSON 触达,新增四个
可选键:alignment/sync/mapping/external(schema 版本仍为 1,
因为这是加法式扩展;未知键依旧一律拒绝)。 - 不指定
--audio-plan时默认音频路径完全不变(-map 0+-c:a copy)。 --audio-plan仍只在经典软件路径(--encoder x265/svtav1)生效;硬件后端
与 Sony/DJI 保留管线明确报错(rc=2)而不是静默忽略,与--channel-sync
互斥(同样 rc=2)。
Package
自包含 Windows x64 包(与 v0.6.1 同一套打包脚本 release/build_release.py):
1KeyTranscoder-v0.8.0-win64-selfcontained.zip
sha256 7eb98a8077bda09f5c7e074198e83fc84aee0782204580923a52de5fe0f5c0ba
522 file records · 内含 release-manifest.json(逐文件 size + sha256)
runtime: ffmpeg/ffprobe 9.0.1 · NVEncC 9.31 · QSVEncC 8.26 · MP4Box 26.02
包完整性由 release/verify_package.py 复核:62/62 checks PASS(含逐文件
sha256 对账、开发产物不得入包、运行时版本可复现)。
Not implemented
drift correction: not implemented
resampling: not implemented
loudness: not implemented
也仍然没有:自动多文件同步(外挂音频只按文件名规则发现,不做内容匹配)、
codec 能力 / quality preset / VBR 策略框架、多 mixer sink 的输出结构、
硬件后端与 Sony/DJI 保留管线上的音频计划、P5。
Unchanged
- video encode backend —
encoders/、core/scaling.py、core/hwdecode.py
相对v0.7.1零改动,视频基本流 sha256 在每种音频处理下都和默认路径一致
(回归逐案断言)。 - metadata backend / Sony / DJI preservation —
preservation/相对v0.7.1
零改动。 - sync algorithm —
core/channel_sync.py/core/sync_estimate.py/
core/sync_fix.py/core/mp4_channel_sync.py零改动;arbitrary-reference
回归全部通过。 - default audio path — 未启用
--audio-plan时行为与 v0.7.1 完全一致。