9.6 KiB
9.6 KiB
本地录制调试指南(第二版)
问题描述
本地录制总是失败,blob 为空,导致无法保存通话录制文件。
根本原因分析
从实际日志分析发现:
- 录制启动太晚:原来的 1200ms 延迟导致录制在对方挂断后才启动
- SDK 提前销毁轨道:
stopLocalVideo/stopLocalAudio在exitRoom之前就销毁了视频轨道 - 时间窗口太短:从对方离开到轨道销毁只有约 200-300ms
关键时间线(问题场景):
15:30:51.841 - 对方离开(USER_LEAVE 事件)
15:30:51.xxx - beginStopNow 被调用(但录制还未启动)
15:30:52.072 - stopLocalVideo/stopLocalAudio(轨道被销毁)
15:30:52.102 - exitRoom
15:30:52.374 - MediaRecorder 已启动(太晚了!)
15:30:52.xxx - 数据块数: 0(因为轨道已销毁)
已实施的修复(第二版)
1. 大幅缩短录制启动延迟
- 从 1200ms 缩短到 500ms
- 使用 300ms 间隔重试(最多 10 次)
- 更快地检测和启动录制
// 延迟 500ms 后开始尝试(给视频元素一点加载时间)
startLocalRecTimer = setTimeout(() => attemptStartRecording(0), 500)
2. 在通话结束事件时立即停止录制
监听 TUICallEngine 事件,在检测到通话结束时立即停止:
const onEarlyStopRecording = () => {
console.log('[chat-dialog] 检测到通话结束事件,立即停止录制')
if (localCallRecorder.isRecording()) {
console.log('[chat-dialog] 录制进行中,触发 beginStopNow')
localCallRecorder.beginStopNow()
}
}
// 监听: CALL_END, USER_LEAVE, ON_CALL_NOT_CONNECTED
3. 缩短停止延迟
- 从 300ms 缩短到 100ms
- 减少等待时间,避免 SDK 提前销毁轨道
// 缩短延迟到 100ms,避免 SDK 提前销毁轨道
setTimeout(() => {
mr.stop()
}, 100)
4. 增强日志输出
在关键位置添加了详细的 console.log:
-
录制启动阶段:
- 检查容器和通话状态
- 视频元素数量和就绪状态
- MediaRecorder 创建和启动状态
- Canvas 和音频混音初始化
-
录制进行阶段:
- 数据块收集(ondataavailable)
- MediaRecorder 状态变化
-
录制停止阶段:
- beginStopNow 调用时机
- 数据块数量
- Blob 生成结果
5. 智能视频元素检测
改进了视频元素查找逻辑:
// 1. 首先检查容器内的视频
let videos = Array.from(root.querySelectorAll('video'))
// 2. 如果没有,检查 body(TUICallKit Teleport)
if (videos.length === 0) {
const bodyVideos = Array.from(document.body.querySelectorAll('video'))
videos = bodyVideos.filter(v => {
const so = v.srcObject
if (!(so instanceof MediaStream)) return false
return so.getVideoTracks().some(t => t.readyState === 'live')
})
}
// 3. 检查视频是否有有效数据
const hasReadyVideo = videos.some(v =>
v.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA
)
调试步骤
1. 打开浏览器控制台
在发起视频通话前,打开浏览器开发者工具(F12),切换到 Console 标签。
2. 发起视频通话
点击视频通话按钮,观察控制台输出。
3. 关键日志检查点
通话接通后(约 0.5-1 秒)
应该看到:
[chat-dialog] 尝试启动本地录制(第 1 次)
[chat-dialog] 在 body 中找到活跃视频: 2
[chat-dialog] 视频元素已就绪,立即启动录制
[call-local-recorder] 使用 MIME 类型: video/webm;codecs=vp9,opus
[call-local-recorder] Canvas 视频轨道数: 1
[call-local-recorder] 找到视频元素数量: 2
[call-local-recorder] 输出流轨道: { video: 1, audio: 2 }
[call-local-recorder] MediaRecorder 创建成功
[call-local-recorder] MediaRecorder.start() 调用成功
[call-local-recorder] MediaRecorder 已启动
[chat-dialog] ✅ 本地录制已成功启动
录制过程中(每 250ms)
应该看到:
[call-local-recorder] 收到数据块,大小: 12345 (总计: 1 块)
[call-local-recorder] 收到数据块,大小: 15678 (总计: 2 块)
...
重要:如果没有看到数据块,说明录制有问题!
对方挂断或己方挂断时
应该看到:
[chat-dialog] 检测到通话结束事件,立即停止录制
[chat-dialog] 录制进行中,触发 beginStopNow
[call-local-recorder] beginStopNow 被调用
[call-local-recorder] 准备停止 MediaRecorder,当前状态: recording 已收集数据块: 15
[call-local-recorder] 调用 requestData()
[call-local-recorder] 延迟后数据块数: 16
[call-local-recorder] 调用 stop()
[call-local-recorder] MediaRecorder onstop 触发,数据块数: 16
[call-local-recorder] 生成 Blob: 1234567 bytes, type: video/webm
[chat-dialog] localCallRecorder.stop() 返回: 1234567 bytes
[chat-dialog] 开始上传录制文件
[chat-dialog] 上传成功,文件 URL: https://...
[chat-dialog] 关联录制文件到诊单
本地录制已上传并关联到通话记录
常见问题诊断
问题 1:录制启动太晚
症状:
[call-local-recorder] MediaRecorder 已启动
(出现在 stopLocalVideo 之后)
原因:
- 视频元素加载慢
- 重试次数不够
解决:
- 已缩短初始延迟到 500ms
- 增加重试次数到 10 次
- 每次重试间隔 300ms
问题 2:找不到视频元素
症状:
[chat-dialog] 未找到视频元素,300ms 后重试
(重复多次)
原因:
- TUICallKit 将视频 Teleport 到 body
- 视频元素尚未渲染
解决:
- 代码已添加 body 视频检查
- 添加了活跃视频流过滤
问题 3:视频元素未就绪
症状:
[chat-dialog] 视频元素未就绪,300ms 后重试
原因:
- 视频流尚未加载
- readyState < HAVE_CURRENT_DATA
解决:
- 代码已添加就绪检查和重试
- 最多重试 10 次
问题 4:没有收到数据块
症状:
- 录制启动成功
- 但从未看到 "收到数据块" 日志
- 最终
数据块数: 0
可能原因:
- Canvas 绘制失败(跨域问题)
- MediaRecorder 不支持当前 MIME 类型
- 视频流被中断或为空
检查方法:
// 在控制台手动检查
const videos = document.querySelectorAll('video')
videos.forEach((v, i) => {
console.log(`Video ${i}:`, {
readyState: v.readyState,
videoWidth: v.videoWidth,
videoHeight: v.videoHeight,
srcObject: v.srcObject,
videoTracks: v.srcObject?.getVideoTracks().map(t => ({
id: t.id,
label: t.label,
readyState: t.readyState,
enabled: t.enabled
})),
audioTracks: v.srcObject?.getAudioTracks().map(t => ({
id: t.id,
label: t.label,
readyState: t.readyState,
enabled: t.enabled
}))
})
})
问题 5:通话时间过短
症状:
[call-local-recorder] 生成 Blob: null
或
[call-local-recorder] 生成 Blob: 1234 bytes(很小)
原因:
- 通话时长 < 1 秒
- 录制启动后立即挂断
解决:
- 确保通话时长 > 2 秒
- 测试时保持通话至少 5 秒
问题 6:SDK 提前销毁轨道
症状:
[call-local-recorder] MediaRecorder 错误
或
数据块数为 0
原因:
- SDK exitRoom 在 MediaRecorder.stop() 之前执行
- 视频轨道被销毁
解决:
- 已在 USER_LEAVE 事件时立即停止录制
- 已在多个挂断入口添加钩子
- 缩短停止延迟到 100ms
测试建议
最小测试场景
- 发起视频通话
- 等待接通(看到对方画面)
- 保持通话至少 5 秒(重要!)
- 正常挂断
- 检查控制台日志和录制结果
关键检查点
- 录制在接通后 1 秒内启动
- 看到 "✅ 本地录制已成功启动"
- 看到持续的 "收到数据块" 日志
- 数据块数 > 0(至少 4-5 块)
- 生成的 Blob 大小 > 10KB
- 上传成功并关联到诊单
压力测试场景
- 快速挂断(2-3 秒)
- 对方挂断
- 网络中断
- 切换会话后挂断
- 关闭聊天窗口
预期改进效果
修复前
通话接通 -> 等待 1200ms -> 尝试启动录制 -> 对方挂断 -> SDK 销毁轨道 -> 录制失败
修复后
通话接通 -> 等待 500ms -> 快速启动录制 -> 开始收集数据 ->
检测到挂断 -> 立即停止录制 -> 生成 Blob -> 上传成功
浏览器兼容性
支持的浏览器
- Chrome/Edge 85+
- Firefox 78+
- Safari 14.1+
MIME 类型优先级
video/webm;codecs=vp9,opus(最佳)video/webm;codecs=vp8,opusvideo/webm;codecs=vp9video/webm(兜底)
检查浏览器支持
const mimes = [
'video/webm;codecs=vp9,opus',
'video/webm;codecs=vp8,opus',
'video/webm;codecs=vp9',
'video/webm'
]
mimes.forEach(m => {
console.log(m, MediaRecorder.isTypeSupported(m))
})
性能优化建议
- Canvas 分辨率:当前 1280x720,可根据需要调整
- 数据块间隔:当前 250ms,可调整为 500ms 减少开销
- 音频混音:如果不需要音频,可以禁用 AudioContext
下一步优化(如果问题仍存在)
- 进一步缩短启动延迟:从 500ms 减少到 300ms
- 增加录制状态监控:定期检查 MediaRecorder 状态
- 添加降级方案:如果本地录制失败,提示用户依赖云端录制
- 添加手动录制控制:让用户手动开始/停止录制
- 使用 MutationObserver:监听视频元素的添加,立即启动录制
联系支持
如果按照本指南操作后问题仍未解决,请提供:
- 完整的控制台日志(从通话开始到结束)
- 浏览器版本和操作系统
- 通话时长(秒)
- 是否看到视频画面
- 是否有任何错误提示
- 数据块数量(如果有)