视频转写全链路实录:从免费额度到说话人分离
2026-10-16
想把一个 7 分钟的访谈视频转成逐字稿,要求带时间戳、区分说话人。我用腾讯云语音识别全链路跑通了:1 分钟出结果,96 句转成 60 个话轮,3915 字。
但链路打通之前,我在 8 个坑里摔了一遍。这篇是完整实录。
为什么做这个
需求:视频转文字稿,两个硬要求——每句话带时间戳、区分说话人。市面上的免费工具要么没时间戳,要么不分说话人,要么把数据传到不知名的服务器。
选型时定了条长期策略:默认用临期/限免的 API,本地做备用。按有效期短的先用:
- 腾讯云录音文件识别(新用户 10 小时包)——自带说话人分离+逐句时间戳,有额度时默认走这条
- 硅基流动 SenseVoiceSmall(待接入)
- 腾讯云一句话识别分块(无说话人分离)
- Groq 免费层
- 本地 faster-whisper + ECAPA(备用链路)
核心逻辑:免费额度过期不候,先用要过期的。10 小时包 10 月 31 号到期,不用白不用。
怎么做的
路线 A:base64 直传(≤5MB 音频)
音频压成 16kHz 单声道、48k 码率的 mp3(7.8 分钟约 2.67MB),base64 编码后直接调 CreateRecTask:
{"EngineModelType": "16k_zh",
"ChannelNum": 1,
"ResTextFormat": 1,
"SourceType": 1, # base64 直传
"Data": base64音频,
"DataLen": len,
"SpeakerDiarization": 1, # 说话人分离
"SpeakerNumber": 0, # 16k 引擎不支持指定人数,0=自动识别
"FilterPunc": 0}
零依赖、最顺手,是默认路线。
路线 B:COS 地址(>5MB 音频)
音频先传对象存储,拿预签名 URL 调 CreateRecTask:
{"EngineModelType": "16k_zh",
"ChannelNum": 1,
"ResTextFormat": 1,
"SourceType": 0, # COS 地址
"Url": 预签名URL,
"SpeakerDiarization": 1,
"SpeakerNumber": 0,
"FilterPunc": 0}
这是 >5MB 音频的逃生通道。
轮询结果
d = call("DescribeTaskStatus", {"TaskId": task_id})["Response"]["Data"]
# d["StatusStr"]: "success" / "failed"
# 成功后取 d["ResultUrl"] 下载结果文本
结果格式是 [开始,结束,说话人ID] 文本,每行一句。
话轮合并
按说话人 ID 合并连续同说话人的句子,输出标准格式:
[00:00:00] 说话人:同一人连续说的完整的一段话
每话轮只有一个开始时间戳,不按固定时长机械切段。说话人名字按内容推断,交付时必须说明是推断的。
实测数据
抖音访谈视频(465.9 秒),base64 路线:
- 音频:2.67MB mp3
- 任务提交到成功:约 1 分钟
- 结果:96 句 → 60 话轮 → 3915 字
- 花费:免费包约 0.13 小时
踩过的坑
坑 1:AppId ≠ 账号 UIN
现象:桶名后缀用了账号编号,create_bucket 报 AccessDenied,但 list_buckets 却是 200。
原因:腾讯云的 AppId 和账号 UIN 是两个不同的东西。桶名必须为 桶名-AppId。
解法:去控制台右上角头像→账号信息页查真实 AppId(本账号 AppId=1503285150,UIN=100053571078)。
教训:云厂商文档里"账号 ID"有好几种,动手前先确认用的是哪一个。
坑 2:新账号 COS 未在控制台开通
现象:create_bucket 报 AccessDenied。
原因:对象存储要在控制台手动点"开通",API 开不了。
解法:控制台手动开通,开通即领 50GB 标准存储免费 180 天。
坑 3:SourceType 没有 2
现象:CreateRecTask 报 InvalidParameter: SourceType: 2 not in list: [0,1]。
原因:想当然以为 COS 地址是 SourceType=2。
解法:COS 地址方式 = SourceType=0 + Url;base64 = SourceType=1。就两个值。
坑 4:私有桶裸 URL
现象:ASR 任务 failed,ErrorMsg: Failed to download audio file!。
原因:私有桶的裸 URL 外部访问不了。
解法:用 get_presigned_url 生成预签名 URL(有效期 1 小时)再提交。
坑 5:本机直连 COS 被墙
现象:SSL: WRONG_VERSION_NUMBER。
解法:必须走代理(保留 https_proxy 环境变量);CosConfig 的 Timeout 设 120 秒以上(代理传 2.8MB 约 98 秒,默认 30 秒必超时)。
坑 6:返回结构认错层级
现象:KeyError: 'TaskId'。
原因:CreateRecTask 和 DescribeTaskStatus 的返回都在 Response.Data 下,不是 Response 直属。
教训:调新 API 先打印完整返回再写解析代码,5 分钟能避的坑。
坑 7:16k 引擎指定说话人数
现象:传 SpeakerNumber=2 不生效。
原因:16k 引擎不支持指定人数。
解法:固定传 0(自动识别)。
坑 8:base64 单文件超限
现象:服务端拒收。
解法:单文件 ≤5MB;超了就压 48k mp3 或改走路线 B。
总结
- 免费额度按"有效期短先用":过期不候的东西,不用就是浪费
- 云厂商的"账号 ID"有好几种,动手前先确认用的是哪一个(AppId≠UIN)
- 调新 API 先打印完整返回再写解析,不要靠猜结构
- 转写质量由引擎层保证,与传输方式无关——选路线只看文件大小和依赖成本
- base64 直传是默认路线,COS 地址是逃生通道,两条都验证过才算链路打通
以上,全文完。
最后认识一下,我是 Liyu,一名 AI 实践爱好者,会不定期记录我的学习过程,期待你的关注和分享。