guoyu/视频异常处理逻辑.md
2025-12-11 23:28:07 +08:00

396 lines
9.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 视频异常处理逻辑总结
## 📊 **整体流程**
```
视频加载 → URL测试 → 元数据加载 → 黑屏检测 → 播放 → 错误处理
```
---
## 1⃣ **视频URL测试testVideoUrl**
### **触发时机:**
- 加载视频课件时(`loadCourseware`
### **处理逻辑:**
```javascript
async testVideoUrl(url) {
// 使用HEAD请求测试文件是否存在不下载文件内容
const res = await uni.request({
url: url,
method: 'HEAD',
timeout: 5000
})
if (res.statusCode === 200) {
视频文件可访问
- 记录Content-Type
- 记录Content-Length
} else if (res.statusCode === 404) {
视频文件不存在(404)
- 检查数据库filePath是否正确
} else if (res.statusCode === 403) {
没有权限访问(403)
} else {
⚠️ 其他响应码
}
}
```
### **作用:**
- ✅ 提前检测视频是否可访问
- ✅ 输出详细日志,便于调试
- ⚠️ **只是日志输出,不会阻止播放**
---
## 2⃣ **黑屏检测与自动修复onVideoLoadedMetadata**
### **触发时机:**
- 视频元数据加载完成时
### **检测逻辑:**
```javascript
if (e.detail.width === 0 || e.detail.height === 0) {
⚠️ 检测到视频尺寸异常黑屏
if (videoRetryCount < 3) {
// 自动重试修复
videoRetryCount++
// 强制重新加载视频
this.videoId = 'course-video-' + Date.now() // 重新生成videoId
this.videoUrl = ''
await this.$nextTick()
this.videoUrl = currentUrl
this.initialTime = currentInitialTime
尝试修复黑屏最多3次
} else {
已达到最大重试次数放弃修复
显示提示'视频加载异常,请退出重试'
}
} else {
视频尺寸正常重置重试计数器
videoRetryCount = 0
}
```
### **修复机制:**
1. **检测**宽度或高度为0 → 黑屏
2. **重试**最多3次
3. **方法**重新生成videoId强制组件重建
4. **失败**:显示提示,要求用户退出重试
---
## 3⃣ **播放错误处理onVideoError**
### **触发时机:**
- 视频播放过程中发生错误
### **错误分类与处理:**
#### **A. 根据错误码判断**
```javascript
if (e.detail?.errCode === -1) {
视频文件不存在或无法访问
} else if (e.detail?.errCode === -2) {
视频格式不支持
} else if (errMsg.includes('404')) {
视频文件未找到(404)
} else if (errMsg.includes('403')) {
没有权限访问视频文件(403)
} else if (errMsg.includes('timeout')) {
视频加载超时请检查网络
}
```
#### **B. 自动尝试URL编码切换**
```javascript
// 构建原始URL未编码
const originalUrl = config.FILE_BASE_URL + filePath
// 如果当前URL是编码的尝试未编码版本
if (this.videoUrl !== originalUrl) {
console.log('尝试切换到未编码的URL')
this.videoUrl = originalUrl
// 等待500ms后重新尝试播放
setTimeout(() => {
const videoContext = uni.createVideoContext('course-video')
videoContext.play()
videoContext.pause()
}, 500)
} else {
// 已经尝试过未编码版本,显示错误提示
显示Modal
- 标题'视频播放失败'
- 内容错误信息 + 服务器地址 + 文件路径
}
```
#### **C. 显示详细错误信息**
```javascript
uni.showModal({
title: '视频播放失败',
content: `
${errorMsg}
请检查:
1. 服务器是否运行
2. 视频文件是否存在
3. 网络连接是否正常
服务器: ${config.FILE_BASE_URL}
路径: ${courseware.filePath}
`,
showCancel: false,
confirmText: '知道了'
})
```
---
## 4⃣ **视频加载失败的完整处理流程**
```mermaid
graph TD
A[开始加载视频] --> B{URL测试}
B -->|200 OK| C[加载视频]
B -->|404/403/其他| D[输出日志,继续尝试加载]
C --> E{元数据加载}
E -->|成功| F{尺寸检测}
F -->|正常| G[开始播放]
F -->|黑屏| H{重试次数<3?}
H -->|是| I[重新加载视频]
I --> C
H -->|否| J[提示用户:请退出重试]
E -->|失败| K[触发onVideoError]
G --> L{播放过程}
L -->|正常| M[播放完成]
L -->|错误| K
K --> N{URL编码尝试}
N -->|未尝试| O[切换到未编码URL]
O --> C
N -->|已尝试| P[显示错误提示Modal]
```
---
## 5⃣ **不能播放的视频如何处理?**
### **A. 自动处理(无需用户干预)**
#### **1. 黑屏问题**
-**自动重试3次**
-**每次间隔300ms**
-**成功后重置计数器**
-**3次失败后提示用户**
#### **2. URL编码问题**
-**自动尝试未编码版本**
-**间隔500ms后重试**
-**失败后显示Modal**
---
### **B. 需要用户干预**
#### **1. 文件不存在404**
```
结果显示Modal
提示:视频文件未找到(404)
服务器: http://xxx
路径: /profile/upload/xxx.mp4
建议:
- 检查服务器是否运行
- 检查视频文件是否存在
- 检查filePath是否正确
```
#### **2. 权限问题403**
```
结果显示Modal
提示:没有权限访问视频文件(403)
建议:
- 检查服务器权限配置
- 检查防火墙设置
```
#### **3. 网络超时**
```
结果显示Modal
提示:视频加载超时,请检查网络
建议:
- 检查网络连接
- 检查服务器响应速度
```
#### **4. 格式不支持(-2**
```
结果显示Modal
提示:视频格式不支持
建议:
- 转换视频格式为MP4
- 确保编码为H.264
```
---
### **C. 不影响学习进度**
#### **关键逻辑:**
```javascript
// 即使视频无法播放,图片/PDF仍然可以正常学习
// 进度计算:已完成课件数 / 总课件数
if ("video".equals(cw.getType())) {
if (detail != null && detail.getVideoPosition() != null) {
// 有观看记录才计入完成
if (videoPosition >= duration * 0.8) {
completedCount++
}
}
} else {
// 图片/PDF查看过视为完成
if (detail != null) {
completedCount++ // ✅ 不影响
}
}
```
#### **示例:**
```
课程有5个视频 + 3个图片
情况1所有视频都能播放
- 看完3个视频 + 3个图片 → 进度 = 6/8 = 75%
情况2有2个视频无法播放
- 看完3个视频 + 3个图片 → 进度 = 6/8 = 75%
- 无法播放的2个视频不计入除非有观看记录
情况3所有视频都无法播放
- 看完3个图片 → 进度 = 3/8 = 37.5%
- 用户仍可学习图片课件
```
---
## 6⃣ **日志输出(便于调试)**
### **加载阶段:**
```
[课程学习] 📦 加载课件数据: {...}
[课程学习] 课件ID: 893
[课程学习] 课件类型: video
[课程学习] 🔗 URL构建信息:
- FILE_BASE_URL: http://localhost:8080
- 原始filePath: /profile/upload/xxx.mp4
- 完整URL: http://localhost:8080/profile/upload/xxx.mp4
[课程学习] 🧪 测试视频URL可访问性: ...
[课程学习] ✅ 视频文件可访问
```
### **元数据加载:**
```
[课程学习] ✅ 视频元数据加载完成
[课程学习] 视频时长: 300 秒
[课程学习] 视频宽度: 1920
[课程学习] 视频高度: 1080
[课程学习] ✅ 视频尺寸正常,重置重试计数器
```
### **错误阶段:**
```
[课程学习] ❌❌❌ 视频播放错误 ❌❌❌
[课程学习] 错误码: -1
[课程学习] 错误信息: file not found
[课程学习] 视频URL: http://localhost:8080/profile/upload/xxx.mp4
[课程学习] 完整错误对象: {...}
```
---
## 7⃣ **总结**
### **自动处理机制:**
1.**URL测试**:提前检测可访问性
2.**黑屏修复**最多3次自动重试
3.**编码切换**自动尝试未编码URL
4.**详细日志**:便于定位问题
### **用户体验:**
- ✅ 大部分问题自动修复,无需用户干预
- ✅ 修复失败时显示详细错误信息和建议
- ✅ 不影响其他课件的学习
- ✅ 进度计算正确,不会因个别视频问题阻塞
### **不影响进度:**
- ✅ 无法播放的视频不计入完成
- ✅ 图片/PDF可正常学习并计入进度
- ✅ 用户可跳过问题视频,继续学习其他内容
---
## 🔧 **管理员排查步骤**
### **1. 检查后端日志**
```bash
# 查看课件信息
[INFO] 视频课件 893 (测试视频): 时长=300秒
[WARN] 视频课件 894 时长信息缺失或无效: null
# 查看进度计算
[INFO] 课程 123 进度计算完成: 5个已完成课件 / 8个总课件 = 62.5%
```
### **2. 检查前端日志**
```bash
# 查看URL构建
[课程学习] 🔗 URL构建信息:
- FILE_BASE_URL: http://localhost:8080
- 原始filePath: /profile/upload/2025/12/11/xxx.mp4
# 查看错误信息
[课程学习] ❌ 视频文件不存在(404)
[课程学习] 请检查数据库中的filePath是否正确
```
### **3. 验证文件**
```bash
# 检查文件是否存在
ls -lh /path/to/upload/profile/upload/2025/12/11/xxx.mp4
# 检查文件权限
chmod 644 /path/to/upload/profile/upload/2025/12/11/xxx.mp4
```
### **4. 测试URL**
```bash
# 使用curl测试
curl -I http://localhost:8080/profile/upload/2025/12/11/xxx.mp4
# 预期结果HTTP/1.1 200 OK
```
---
**最后更新:** 2025-12-11
**文档版本:** v1.0