添加音轨到录屏流后MediaRecorder录制的webm文件损坏
录屏录制文件损坏问题说明
问题背景
- 已完成屏幕共享流的录制功能,实现逻辑为录制流分片上传至服务端、服务端拼接存储为webm/mp4格式文件
- 为录屏流追加额外媒体轨时先后尝试两种实现方案:
- 调用
addTracks方法为现有录屏流追加本地采集的音频轨 - 新建独立
MediaStream实例,整合录屏流的视频轨与本地采集的音频轨
- 调用
- 两种方案最终均触发录制文件损坏的问题,和之前单录屏无音频时的问题表现一致
补充参考:MediaRecorder API 的详细说明可通过 MDN 公开文档获取
补充约束:若建议改用WebRTC实现,需要同步解决此前场景下WebRTC录制文件损坏的问题
现有实现代码
客户端代码
startMedia = () => { this.setState({ mediaState: "pending" }) navigator.mediaDevices.getDisplayMedia({ video: true }).then(async (screenSharingStream) => { console.log(MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus')) const socketIO = io.connect(baseURL, { query: { candidateID: this.candidateID, roundTest: this.roundTest } }) const mediaStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }).catch(e => { throw e }); const mediaRecorderOptions = { videoBitsPerSecond: 128000, audioBitsPerSecond: 128000, mimeType: 'video/webm; codecs=vp8,opus' } const [videoTrack] = screenSharingStream.getVideoTracks(); const [audioTrack] = mediaStream.getAudioTracks(); if (audioTrack && videoTrack) videoTrack.addTrack(audioTrack) const stream = new MediaStream([videoTrack, audioTrack]); this.socketRef.current = socketIO; this.mediaStream = mediaStream this.screenSharingStream = stream this.candidateVideoRef.current.srcObject = this.mediaStream; this.mediaRecorder = new MediaRecorder(this.screenSharingStream, mediaRecorderOptions) this.mediaRecorder.ondataavailable = function (event) { if (event && event.data.size > 0) { const reader = new FileReader(); reader.onload = function () { const dataURL = reader.result; console.log('van chay') const base64EncodedData = dataURL.split(',')[1]; socketIO.emit('SEND BLOB', base64EncodedData) } reader.readAsDataURL(event.data) } } this.mediaRecorder.start(1000) this.setState({ mediaState: this.mediaRecorder.state }) }).catch(err => { console.log(err.name) switch (err.name) { case 'NotAllowedError': message.error('候选人端未授权录屏权限') this.setState({ mediaState: "Aborting" }) break; default: message.error('系统异常,请联系管理员') this.setState({ mediaState: "Aborting" }) break; } }) } stopMedia = () => { if (this.mediaStream) { this.mediaStream.getTracks().forEach((track) => { if (track.readyState === 'live') { track.stop() this.candidateVideoRef.current.style.display = 'none'; } }) } if (this.screenSharingStream) { this.mediaRecorder.stop() this.setState({ mediaState: this.mediaRecorder.state }) } }
服务端代码
socket.on("SEND BLOB", chunk => { try { const fileExtension = '.webm' const dataBuffer = new Buffer(chunk, 'base64'); const fileStream = fs.createWriteStream( path.join(__dirname, './videos/candidate/', candidateID + '-' + roundTest + fileExtension), { flags: 'a' } ); fileStream.write(dataBuffer); } catch (e) { console.log(e) } })
内容的提问来源于stack exchange,提问作者KelNguyen
相关产品推荐
相关产品推荐

