const mediaRecorder = ref()
/**
* 停止录制
*/
const stopRecord = () => {
recordButLoading.value = true
TimerVisible.value = false
RecorderVisible.value = true
mediaRecorder.value?.stop()
}
const startRecord = async (data: any, stream: any) => {
if (!stream) {
return
}
openIndexDB()
recordedBlobs.value = []
currentVideoIndex.value = 0
// === 编码器检测逻辑 ===
const supportedCodecs = MediaRecorder.isTypeSupported
let codecOptions
// 检测支持的编解码器
if (supportedCodecs('video/webm; codecs=vp9,opus')) {
codecOptions = 'video/webm; codecs=vp9,opus'
} else if (supportedCodecs('video/webm; codecs=vp8,opus')) {
codecOptions = 'video/webm; codecs=vp8,opus'
} else {
codecOptions = 'video/webm;codecs=opus'
}
console.log('设置的解码器:', codecOptions)
const options = { mimeType: codecOptions }
mediaRecorder.value = new MediaRecorder(stream, options)
// 监听停止录制事件
const nowTime = Date.now()
currentVideoName.value = `${nowTime}.webm`
// 10s判断一下累计的大小是都大于5M
mediaRecorder.value.start(10000)
RecorderVisible.value = false
TimerVisible.value = true
// 从后端根据文件名称获取uploadId
await getUploadIdByFileName()
let recordedSize = 0
mediaRecorder.value.ondataavailable = (event: any) => {
// 点击停止之后,先处理ondataavailable之后是onstop
recordedSize += event.data.size
if (event.data && event.data.size > 0) {
recordedBlobs.value.push(event.data)
}
// 最后一片或是大于5M就作为一片
if (recordedSize > 6 * 1024 * 1024 || !TimerVisible.value) {
currentVideoIndex.value += 1
const tempData = recordedBlobs.value
console.log('存储一次分片的数据')
handleDataAvailable(tempData)
recordedBlobs.value = []
recordedSize = 0
}
}
mediaRecorder.value.onstop = () => {
recordButLoading.value = false
RecorderVisible.value = true
if (!successOpenDB.value) {
return
}
// 开启事务,并获取数据存储对象
const transaction = recordDB.transaction(
[SLICE_VIDEO_LIST_NAME],
'readwrite'
)
const objectStore = transaction.objectStore(SLICE_VIDEO_LIST_NAME)
// 使用索引查询 fileName为 'ABC123' 的数据
const index = objectStore.index('fileNameIndex')
const updateFinish = index.getAll(
IDBKeyRange.only(currentVideoName.value)
)
updateFinish.onsuccess = function (event: any) {
const records = event.target.result
createSuccessMessage('视频录制成功')
if (records && records.length > 0) {
records.forEach(function (record: any) {
// 更新记录的 recordFinish 属性为 true
record.recordFinish = true
// 更新记录
const updateRequest = objectStore.put(record)
updateRequest.onsuccess = function () {
console.log('停止之后记录更新成功')
}
updateRequest.onerror = function () {
console.error('停止之后记录更新失败')
}
})
} else {
console.log('未找到匹配的记录')
}
}
updateFinish.onerror = function (event: any) {
console.error('查询失败', event.target.error)
}
}
}
MediaRecorder 方法解析
1. mediaRecorder.value.start(10000)
-
启动媒体录制
-
参数
10000
表示每 10 秒(10000 毫秒)触发一次ondataavailable
事件 -
这样设计是为了定期获取录制的视频数据块
2. mediaRecorder.value.stop()
-
停止媒体录制
-
会触发最后的
ondataavailable
事件(获取最后的数据块) -
然后触发
onstop
事件
3. mediaRecorder.value.ondataavailable
-
事件处理器,当有新的录制数据可用时触发
-
主要功能:
-
累计记录数据大小 (
recordedSize
) -
将数据块存入
recordedBlobs
数组 -
判断条件(大于6MB或录制停止)时调用
handleDataAvailable
处理数据 -
重置记录状态
-
4. mediaRecorder.value.onstop
-
事件处理器,当录制完全停止时触发
-
主要功能:
-
更新界面状态
-
检查 IndexedDB 是否成功打开
-
更新 IndexedDB 中该视频的完成状态
-
updateFinish
解析
updateFinish
是 IndexedDB 的一个查询操作,主要作用是:
-
通过
fileNameIndex
索引查询所有fileName
等于currentVideoName.value
的记录 -
查询成功后 (
onsuccess
):-
遍历所有匹配的记录
-
将每条记录的
recordFinish
属性设置为true
(标记为已完成) -
使用
objectStore.put()
更新记录
-
-
如果没有找到匹配记录,输出日志
-
查询失败时 (
onerror
) 输出错误信息
整体流程
-
开始录制:
-
检测支持的视频编解码器
-
创建 MediaRecorder 实例
-
每10秒获取一次数据块
-
当数据块累计超过6MB或停止录制时,处理并存储数据
-
-
停止录制:
-
停止 MediaRecorder
-
处理最后的数据块
-
更新 IndexedDB 中该视频的状态为已完成
-