如何正确录制Canvas视频?解决时长异常与WhatsApp分享失真问题
Canvas录制视频时长过短+WhatsApp分享失真解决方案
问题背景
使用Media Recorder API录制HTML Canvas,完成后上传服务器,再通过Web Share API分享至WhatsApp等应用。已实现基础录制与上传功能,但存在两个核心问题:
- 录制的视频时长过短,与实际操作时长不符
- 分享至WhatsApp后视频画面失真
问题1:录制视频时长过短的修复
核心原因
- 在
ondataavailable回调中错误设置e.target.videoBitsPerSecond=10,极低码率导致录制数据异常 - 依赖
setTimeout+手动计时计算视频时长,未等待MediaRecorder完成数据收尾 - Canvas未持续绘制时,
captureStream会停止推送帧,导致流中断
修复代码
let recording = false; let mediaRecorder; let recordedChunks = []; recordBtn.addEventListener("click", () => { recording = !recording; if(recording){ recordBtn.textContent = "Stop"; // 维持Canvas持续绘制,避免流中断 const drawLoop = () => { if(recording) requestAnimationFrame(drawLoop); }; drawLoop(); const stream = can.captureStream(25); // 初始化时设置合理编码参数,优先选兼容性更好的VP8 mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm;codecs=vp8', ignoreMutedMedia: true, videoBitsPerSecond: 4000000, audioBitsPerSecond: 128000 }); recordedChunks = []; mediaRecorder.ondataavailable = e => { if(e.data.size > 0){ recordedChunks.push(e.data); } }; // 监听stop事件,确保所有数据块收集完成再处理 mediaRecorder.onstop = () => { const blob = new Blob(recordedChunks, { type: "video/webm" }); const data = new FormData(); data.append('file', blob, 'canvas-recording.webm'); $.ajax({ type: 'POST', data: data, contentType: false, processData: false, url: 'http://hthbeats.pythonanywhere.com/file' }).then(function( data ) { console.log('上传成功', data); }); }; mediaRecorder.start(); } else { recordBtn.textContent = "Record"; mediaRecorder.stop(); } });
问题2:WhatsApp分享视频失真的修复
核心原因
- VP9编码的WebM格式在WhatsApp中兼容性差,平台转码时易导致画面失真
- WebM并非社交平台主流支持格式,优先选择H.264编码的MP4格式
修复方案
方案1:直接录制MP4(浏览器支持时)
先检测浏览器支持的MIME类型,优先选择H.264编码的MP4:
function getSupportedMimeType() { const options = [ 'video/mp4;codecs=h264', 'video/webm;codecs=vp8', 'video/webm;codecs=vp9' ]; for(const mimeType of options) { if(MediaRecorder.isTypeSupported(mimeType)) { return mimeType; } } return null; } // 录制时使用 const stream = can.captureStream(25); const mimeType = getSupportedMimeType(); mediaRecorder = new MediaRecorder(stream, { mimeType, ignoreMutedMedia: true, videoBitsPerSecond: 4000000, audioBitsPerSecond: 128000 });
方案2:前端转码为MP4(浏览器不支持直接录制时)
使用FFmpeg.wasm将WebM转码为MP4:
mediaRecorder.onstop = async () => { const webmBlob = new Blob(recordedChunks, { type: "video/webm" }); const ffmpeg = FFmpeg.createFFmpeg({ log: true }); await ffmpeg.load(); ffmpeg.FS('writeFile', 'input.webm', await fetch(webmBlob).then(r => r.arrayBuffer())); // H.264编码+AAC音频,适配WhatsApp await ffmpeg.run('-i', 'input.webm', '-c:v', 'libx264', '-crf', '23', '-preset', 'medium', '-c:a', 'aac', '-b:a', '128k', 'output.mp4'); const mp4Data = ffmpeg.FS('readFile', 'output.mp4'); const mp4Blob = new Blob([mp4Data.buffer], { type: 'video/mp4' }); // 上传转码后的MP4 const data = new FormData(); data.append('file', mp4Blob, 'canvas-recording.mp4'); $.ajax({ type: 'POST', data: data, contentType: false, processData: false, url: 'http://hthbeats.pythonanywhere.com/file' }).then(function( data ) { console.log('上传成功', data); }); };
修改分享代码(适配MP4)
async function m(){ try { let blob = await fetch("https://hthbeats.pythonanywhere.com/static/t.mp4").then(r => r.blob()); const file = new File([blob], "canvas-recording.mp4", {type: 'video/mp4'}); const filesArray = [file]; const shareData = { files: filesArray, title: 'HTh-Beats', text: 'Video sharing ', url: "https://hthbeats.pythonanywhere.com/static/t.mp4" }; await navigator.share(shareData); console.log('分享成功'); } catch(err) { console.log('分享取消或失败', err); } }
内容的提问来源于stack exchange,提问作者Mahesh Mishra
相关产品推荐
相关产品推荐

