视频录制实战:从摄像头到屏幕的全面捕获

大家好,我是你们的老朋友。今天咱们来聊聊 WebRTC 里最实用的一个场景——视频录制。说实话,我刚开始接触这块时,也被 MediaRecorder 的坑折腾过好几回。但别担心,跟着我一步步来,你很快就能搞定。

核心知识点:MediaRecorder API + MediaStream 的组合使用

14.1 录制摄像头画面

先来个最基础的——录制摄像头。说白了,就是三步走:获取流、创建录制器、处理数据。

// 第一步:获取摄像头流
const stream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
});

// 第二步:创建 MediaRecorder
const recorder = new MediaRecorder(stream, {
  mimeType: 'video/webm;codecs=vp9'
});

// 第三步:收集数据块
const chunks = [];
recorder.ondataavailable = (e) => {
  if (e.data.size > 0) chunks.push(e.data);
};

recorder.onstop = () => {
  const blob = new Blob(chunks, { type: 'video/webm' });
  const url = URL.createObjectURL(blob);
  // 这里可以下载或预览
};

// 开始录制
recorder.start(1000); // 每秒触发一次 ondataavailable

我的经验:mimeType 建议先用 'video/webm;codecs=vp9',如果浏览器不支持再降级到 vp8。我在项目中遇到过 Safari 不支持 webm 的情况,那时得用 'video/mp4'。

14.2 录制屏幕(getDisplayMedia)

录制屏幕和录制摄像头其实很像,只是获取流的方式不同。这里用 getDisplayMedia 代替 getUserMedia

// 获取屏幕流
const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: {
    cursor: 'always'  // 显示鼠标指针
  },
  audio: false  // 屏幕流通常不带音频
});

// 剩下的录制逻辑和上面一样
const recorder = new MediaRecorder(screenStream);
// ... 后续代码相同

注意:getDisplayMedia 必须由用户手势触发(比如点击按钮),不能自动调用。我曾经在页面加载时直接调用,结果被浏览器无情拒绝。

14.3 同时录制摄像头+屏幕画中画

这个场景比较有意思——你想录制屏幕,同时把自己的人脸也放进去。嗯,这里需要用到 Canvas 来合成两路流。

// 获取两路流
const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });

// 创建 Canvas 合成
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 1920;
canvas.height = 1080;

// 把两路流画到 canvas 上
const screenVideo = document.createElement('video');
screenVideo.srcObject = screenStream;
await screenVideo.play();

const cameraVideo = document.createElement('video');
cameraVideo.srcObject = cameraStream;
await cameraVideo.play();

// 定时绘制
function draw() {
  ctx.drawImage(screenVideo, 0, 0, canvas.width, canvas.height);
  // 画中画放在右下角,缩小到 1/4 大小
  ctx.drawImage(cameraVideo, 
    canvas.width - 320, canvas.height - 240, 
    320, 240
  );
  requestAnimationFrame(draw);
}
draw();

// 从 canvas 获取合成流
const mixedStream = canvas.captureStream(30); // 30fps

// 录制合成流
const recorder = new MediaRecorder(mixedStream);
// ... 后续录制逻辑

关键点:canvas.captureStream() 是 WebRTC 里非常强大的工具,它能把任何 canvas 动画变成实时视频流。

14.4 录制音频流

有时候你只需要录声音,比如做播客或者语音笔记。这个反而最简单。

// 获取麦克风流
const audioStream = await navigator.mediaDevices.getUserMedia({
  audio: {
    echoCancellation: true,
    noiseSuppression: true,
    sampleRate: 44100
  },
  video: false
});

// 录制纯音频
const recorder = new MediaRecorder(audioStream, {
  mimeType: 'audio/webm;codecs=opus'
});

// 或者录制为 WAV 格式(需要额外处理)
// 这里推荐直接用 webm,兼容性好

避坑指南:我曾经在录制音频时忘记设置 echoCancellation,结果录出来的声音有严重的回声。建议默认开启这两个降噪选项。

知识体系总览

下面这张图帮你理清今天讲的所有内容:

WebRTC 录制实战知识体系 MediaRecorder 📷 摄像头录制 🖥️ 屏幕录制 🖼️ 画中画合成 🎤 音频录制 底层技术栈 getUserMedia getDisplayMedia canvas.captureStream

常见问题与解决方案

问题 原因 解决方法
录制的视频没有声音 MediaRecorder 未捕获音频轨道 检查 stream.getAudioTracks() 是否为空
录制文件太大 码率设置过高 设置 videoBitsPerSecond 参数,比如 2.5Mbps
画中画画面卡顿 Canvas 绘制频率太高 使用 requestAnimationFrame 代替 setInterval
浏览器不支持 webm Safari 等浏览器限制 使用 MediaRecorder.isTypeSupported() 检测

总结一下:今天咱们从最基础的摄像头录制,到屏幕录制,再到画中画合成,最后到纯音频录制,把 WebRTC 录制的四种常见场景都过了一遍。核心就是 MediaRecorder + MediaStream 的组合拳。你想想看,掌握了这些,做个视频会议录制功能、在线教育录课工具,是不是心里就有底了?

公众号:蓝海资料掘金营,微信deep3321