Scribe 语音转文字教程:用 Ofox API 把录音变成 SRT 字幕

通过 Ofox 调用 Scribe,将录音转成文字与逐词时间戳,再生成 SRT 并封装 MP4 字幕轨。附真实音频、原始响应、转换脚本与失败处理。

蓝灰底、浅色卡纸上的打字机线稿、黄色圆形和 Scribe: Audio to Subtitles 标题。

先向 Ofox 的 /v1/audio/transcriptions 上传 WAV 或 MP3,使用 verbose_json 获取转写和真实词级时间戳,再在本地生成 SRT。本文核验的网关接受 JSON 输出,因此不能把上游原生接口的 SRT 参数直接视为 Ofox 已支持的功能。

2026 年 10 月 10 日,我们将一段真实生成的 10.00 秒 ElevenLabs 音频提交给 Scribe。返回文本保留了台词词句,标点略有差异;词时间从 0.14 秒到 9.78 秒。转换器生成三条字幕,并将其放入 MP4 的可选英文字幕轨。这是一次完整调用与封装验证,不是嘈杂会议的识别准确率测试。

工具包里各文件证明什么

下载完整工具包,保留音频、原始 JSON、转换器、SRT 和带可选字幕的 MP4,便于追溯每条字幕的来源。

ElevenLabs 实际生成音频

文件用途不能据此推断
elevenlabs.mp3实际上传的录音真人噪声环境识别表现
narration-transcript.json未修改的 API 响应已人工校对的最终稿
narration.srt本地分组后的字幕所有播放器显示一致
subtitled-selectable.mp4视频、音频与字幕轨字幕已经烧进画面

示例是原创文本的合成语音,没有使用客户会议。替换成采访、课堂或工作录音前,应确认有权将其上传到所选云服务;可以听录音,不等于可以任意上传、公开或分享。

1. 准备录音,保留原始时间线

本次路由接受 WAV、MP3。视频或其他容器先导出音频副本,保留原文件与转换命令,避免日后无法解释字幕偏移。

ffmpeg -i original-video.mp4 -vn -c:a pcm_s16le recording.wav
ffprobe -v error -show_entries format=duration \
  -of default=noprint_wrappers=1:nokey=1 recording.wav

导出 WAV 可能明显增大文件,但不会凭空提升原始录音质量。按路由支持情况选择格式,并核对当前上传限制,不假定任意长录音都能一次提交。

第一次转写前尽量不剪辑。删掉两秒开场后,后面所有时间戳相对原视频都会少两秒。若有意只识别片段,要保存片段起点,回到完整视频时加回该偏移。

多音轨视频要明确选中目标轨道,否则可能上传了解说、译音或空音轨。先探测媒体流并听一下导出的文件,比付费转写后才发现选错轨道更容易处理。

2. 正确构造 multipart 请求

设置 OFOX_API_KEY,安装 Python requests、FFmpeg 与 ffprobe 后运行:

python3 audio_api.py transcribe \
  --input elevenlabs.mp3 \
  --output my-transcript.json

客户端提交 elevenlabs/scribe_v2 和 verbose_json,以二进制打开录音,让请求库自动生成 multipart boundary;它保存输入时长和响应记录,不自动重试结果未知的 POST。

等价 cURL 示例:

curl --fail-with-body --silent --show-error \
  https://api.ofox.io/v1/audio/transcriptions \
  -H "Authorization: Bearer $OFOX_API_KEY" \
  -F 'model=elevenlabs/scribe_v2' \
  -F 'response_format=verbose_json' \
  -F 'file=@elevenlabs.mp3;type=audio/mpeg' \
  --output my-transcript.json

二选一即可,两种都运行会再次调用。不要手动加一个不含 boundary 的 Content-Type: multipart/form-data。密钥放 Authorization,模型和文件放表单字段,二者职责不同。

先查 Ofox Scribe 模型页。ElevenLabs 原生文档描述的是供应商接口,不能据此认定 Ofox 会转发其中全部参数。

3. 先看真实 JSON,再写转换逻辑

本例返回 text、language、duration、usage、logprobs 和 words。每个词包含 word、start、end。其他转写工具可能使用 segments,或在词对象里用 text,不能不看响应就套用旧代码。

第一句是“A clear product video starts with a clear brief.”。最后一个词在 9.78 秒结束,而文件容器长 10.00 秒;末尾可能有静音或编码填充,两个数不必相同。

无需再次调用即可检查保存的响应:

import json
from pathlib import Path
result = json.loads(Path('my-transcript.json').read_text())
print(result['text'])
print(result.get('language'))
print(result.get('usage'))
for item in result.get('words', [])[:5]:
    print(item['start'], item['end'], item['word'])

改标点、姓名或术语前保留原始响应。没有 words 时,纯文本不足以重建精确字幕时间,应该获取支持时间戳的响应,或另做音文对齐,不能把总时长均分后假装是真实识别时间。

本例也没有验证过的说话人身份。文本里出现一个名字,不代表这个人就是声学上识别出的讲话者。会议行动项教程会保留这一区别。

4. 把词组合成可读字幕

每条 SRT 包含序号、起止时间和文本。工具包的 make_subtitles.py 按字符数与持续时间做简单分组,使用组内首词起点和末词终点,不重新发明对齐结果。

python3 make_subtitles.py my-transcript.json my-subtitles.srt

样例第一条:

1
00:00:00,140 --> 00:00:02,980
A clear product video starts with a clear brief.

另外两条覆盖 3.56–7.36 秒、7.42–9.78 秒。第一句后的空隙被保留,不为“填满时间线”而延长字幕。

转换器采用约 58 字符、五秒的分组阈值,只是这个短英文样例的实现选择,不是广播或无障碍通用标准。中文、日文、韩文的断句和词间距不同,还要考虑手机阅读速度及换行,不能机械复用英文规则。

每个边界都应审校。样例第二条以“and”结尾,技术上有效,编辑上却可能不自然。可以把相邻词移到另一条,同时沿用该词真实时间。另存修订版,不覆盖原始转写来隐藏修改。

转换器会拒绝缺失、负数、非有限或起始时间倒退的输入,修订后也拒绝重叠词时间。但格式检查不能保证字幕好读:显示太短、断句不自然仍需编辑判断。

5. 校对文本,同时保留修改来源

对照录音和已批准的素材,区分标点规范化与语义错误。本例短旁白返回相同词句,但省略末尾标点;这与品牌名称识别错、漏掉否定词不是同一种问题。

真实录音优先检查姓名、数字、日期、单位和“不”等否定表达。不能因为幻灯片里写了另一个词,就悄悄替换讲话内容;存在歧义时标注待确认,或请有权限的人复核。

校对表可以记录原词句、建议修改、音频区间、原因和审核状态。原音频、原始 JSON、编辑后 SRT 分开保存,别人问起为什么改动时,能定位具体片段,而不是仅引用另一段模型摘要。

6. 添加字幕轨并核对封装

下面保留现有音视频,添加可选 MP4 字幕轨:

ffmpeg -i narration-video.mp4 -i my-subtitles.srt \
  -map 0:v:0 -map 0:a:0 -map 1:0 \
  -c:v copy -c:a copy -c:s mov_text \
  -metadata:s:s:0 language=eng \
  -disposition:s:0 default subtitled-selectable.mp4

语言代码应对应字幕。这不是烧录字幕,部分网页播放器即使能播放视频,也可能忽略此文本轨;设为 default 只是偏好,不保证所有平台自动显示。

本次十秒成品包含 H.264、AAC 和 mov_text。画面取自此前教程演示视频,配上本次真实旁白,展示的是封装流程,不是 API 生成了视频画面。

检查轨道并导出回读:

ffprobe -v error -show_entries stream=codec_type,codec_name \
  -of json subtitled-selectable.mp4
ffmpeg -i subtitled-selectable.mp4 -map 0:s:0 \
  recovered-subtitles.srt

烧录字幕需要支持文字渲染的工具并重新编码。本次 FFmpeg 构建不支持尝试的字幕渲染过滤器,因此交付的是可选轨道版本,未冒称字幕烧录或播放器视觉验收通过。发往具体平台前,仍应检查实际上传后的显示结果。

7. 按问题所在环节排查

格式不支持时核对网关接受的输入、输出,而不是直接认定 Scribe 原生能力缺失。修正格式后再请求,避免重复提交同一个错误输入。

额度 401 应保留完整错误与请求 ID。本项目早期上游路由出错,恢复后新调用成功;钱包余额为正,仍不能证明某次请求用了哪个供应商账号。

字幕整体固定偏移,先检查是否裁掉了片头;偏移逐渐扩大,则检查是否使用不同剪辑或播放速度。确认音频与视频是否同一时间线后再修字幕,不要靠逐条猜测掩盖素材问题。

长录音分段前制定偏移和重叠校对规则。边界可能重复词、丢上下文,本文短文件转换器不自动解决长篇对齐。

8. 区分用量,并保留最终检查

转写用量与音频时长相关,不与返回字数等价。本例容器测得 10.00 秒,而原响应 usage.seconds=10.083265306122449,两者都应保留。末词终点不能代替计费时长,用量字段也不等于已确认结算金额。

完成标准包括:上传正确且获授权的录音、保存成功 JSON、字幕时间来自真实词、文字已经校对、目标播放器显示正确版本。本工具包验证了 API、转换和容器;面向观众的播放效果仍需在自己的发布环境确认。

常见问题

能直接向这个 Ofox 接口请求 SRT 吗?
本文核验的路由接受 JSON 或 verbose JSON,因此使用真实词时间戳在本地转换,不假定原生 SRT 参数已被转发。
有文本却没有 words 怎么办?
保留转写,但不要猜时间;先获取可用的时间戳响应,或进行单独音文对齐。
下载的 MP4 是烧录字幕吗?
不是。它含可选 mov_text 字幕轨;烧录字幕需要把文字渲染进画面。
本例能证明会议识别准确吗?
不能。这是短、干净的合成旁白,真实会议的噪声、重叠发言、姓名和说话人歧义要另行测试。