新增功能
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
# 本地录制调试指南(第二版)
|
||||
|
||||
## 问题描述
|
||||
本地录制总是失败,`blob` 为空,导致无法保存通话录制文件。
|
||||
|
||||
## 根本原因分析
|
||||
|
||||
从实际日志分析发现:
|
||||
1. **录制启动太晚**:原来的 1200ms 延迟导致录制在对方挂断后才启动
|
||||
2. **SDK 提前销毁轨道**:`stopLocalVideo/stopLocalAudio` 在 `exitRoom` 之前就销毁了视频轨道
|
||||
3. **时间窗口太短**:从对方离开到轨道销毁只有约 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 次)
|
||||
- 更快地检测和启动录制
|
||||
|
||||
```typescript
|
||||
// 延迟 500ms 后开始尝试(给视频元素一点加载时间)
|
||||
startLocalRecTimer = setTimeout(() => attemptStartRecording(0), 500)
|
||||
```
|
||||
|
||||
### 2. 在通话结束事件时立即停止录制
|
||||
监听 TUICallEngine 事件,在检测到通话结束时立即停止:
|
||||
|
||||
```typescript
|
||||
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 提前销毁轨道
|
||||
|
||||
```typescript
|
||||
// 缩短延迟到 100ms,避免 SDK 提前销毁轨道
|
||||
setTimeout(() => {
|
||||
mr.stop()
|
||||
}, 100)
|
||||
```
|
||||
|
||||
### 4. 增强日志输出
|
||||
在关键位置添加了详细的 console.log:
|
||||
|
||||
- **录制启动阶段**:
|
||||
- 检查容器和通话状态
|
||||
- 视频元素数量和就绪状态
|
||||
- MediaRecorder 创建和启动状态
|
||||
- Canvas 和音频混音初始化
|
||||
|
||||
- **录制进行阶段**:
|
||||
- 数据块收集(ondataavailable)
|
||||
- MediaRecorder 状态变化
|
||||
|
||||
- **录制停止阶段**:
|
||||
- beginStopNow 调用时机
|
||||
- 数据块数量
|
||||
- Blob 生成结果
|
||||
|
||||
### 5. 智能视频元素检测
|
||||
改进了视频元素查找逻辑:
|
||||
|
||||
```typescript
|
||||
// 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`
|
||||
|
||||
**可能原因**:
|
||||
1. Canvas 绘制失败(跨域问题)
|
||||
2. MediaRecorder 不支持当前 MIME 类型
|
||||
3. 视频流被中断或为空
|
||||
|
||||
**检查方法**:
|
||||
```javascript
|
||||
// 在控制台手动检查
|
||||
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
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 最小测试场景
|
||||
1. 发起视频通话
|
||||
2. 等待接通(看到对方画面)
|
||||
3. **保持通话至少 5 秒**(重要!)
|
||||
4. 正常挂断
|
||||
5. 检查控制台日志和录制结果
|
||||
|
||||
### 关键检查点
|
||||
- [ ] 录制在接通后 1 秒内启动
|
||||
- [ ] 看到 "✅ 本地录制已成功启动"
|
||||
- [ ] 看到持续的 "收到数据块" 日志
|
||||
- [ ] 数据块数 > 0(至少 4-5 块)
|
||||
- [ ] 生成的 Blob 大小 > 10KB
|
||||
- [ ] 上传成功并关联到诊单
|
||||
|
||||
### 压力测试场景
|
||||
1. 快速挂断(2-3 秒)
|
||||
2. 对方挂断
|
||||
3. 网络中断
|
||||
4. 切换会话后挂断
|
||||
5. 关闭聊天窗口
|
||||
|
||||
## 预期改进效果
|
||||
|
||||
### 修复前
|
||||
```
|
||||
通话接通 -> 等待 1200ms -> 尝试启动录制 -> 对方挂断 -> SDK 销毁轨道 -> 录制失败
|
||||
```
|
||||
|
||||
### 修复后
|
||||
```
|
||||
通话接通 -> 等待 500ms -> 快速启动录制 -> 开始收集数据 ->
|
||||
检测到挂断 -> 立即停止录制 -> 生成 Blob -> 上传成功
|
||||
```
|
||||
|
||||
## 浏览器兼容性
|
||||
|
||||
### 支持的浏览器
|
||||
- Chrome/Edge 85+
|
||||
- Firefox 78+
|
||||
- Safari 14.1+
|
||||
|
||||
### MIME 类型优先级
|
||||
1. `video/webm;codecs=vp9,opus` (最佳)
|
||||
2. `video/webm;codecs=vp8,opus`
|
||||
3. `video/webm;codecs=vp9`
|
||||
4. `video/webm` (兜底)
|
||||
|
||||
### 检查浏览器支持
|
||||
```javascript
|
||||
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))
|
||||
})
|
||||
```
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
1. **Canvas 分辨率**:当前 1280x720,可根据需要调整
|
||||
2. **数据块间隔**:当前 250ms,可调整为 500ms 减少开销
|
||||
3. **音频混音**:如果不需要音频,可以禁用 AudioContext
|
||||
|
||||
## 下一步优化(如果问题仍存在)
|
||||
|
||||
1. **进一步缩短启动延迟**:从 500ms 减少到 300ms
|
||||
2. **增加录制状态监控**:定期检查 MediaRecorder 状态
|
||||
3. **添加降级方案**:如果本地录制失败,提示用户依赖云端录制
|
||||
4. **添加手动录制控制**:让用户手动开始/停止录制
|
||||
5. **使用 MutationObserver**:监听视频元素的添加,立即启动录制
|
||||
|
||||
## 联系支持
|
||||
|
||||
如果按照本指南操作后问题仍未解决,请提供:
|
||||
1. 完整的控制台日志(从通话开始到结束)
|
||||
2. 浏览器版本和操作系统
|
||||
3. 通话时长(秒)
|
||||
4. 是否看到视频画面
|
||||
5. 是否有任何错误提示
|
||||
6. 数据块数量(如果有)
|
||||
Reference in New Issue
Block a user