录制控制实战:开始、暂停、恢复、停止与时长限制

各位同学,今天我们来聊聊录制控制的核心操作。说实话,这部分内容在 WebRTC 开发中特别实用。我刚开始做录制功能时,以为就是点一下开始、再点一下停止那么简单。结果呢?用户反馈说「录到一半想暂停怎么办?」、「录了俩小时才发现没设时长限制,文件大得打不开」……嗯,这些坑我都踩过。

今天这一章,我们就来系统地把这些控制逻辑捋清楚。你想想看,一个完整的录制器,至少需要这几个按钮:开始、暂停、恢复、停止。再加上一个时长限制,基本就齐活了。

1. 开始录制:从 MediaRecorder 说起

开始录制,说白了就是创建一个 MediaRecorder 实例,然后把媒体流喂给它。我个人习惯先检查一下浏览器是否支持:

if (!window.MediaRecorder) {
  alert('您的浏览器不支持录制功能,请使用 Chrome 或 Firefox');
  return;
}

const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
const recorder = new MediaRecorder(stream, {
  mimeType: 'video/webm;codecs=vp9,opus'
});
recorder.start();

这里有个小细节——mimeType 最好做个兼容判断。我在项目中遇到过,某些安卓浏览器不支持 vp9,会直接抛异常。所以稳妥的做法是先检测:

const mimeType = MediaRecorder.isTypeSupported('video/webm;codecs=vp9,opus')
  ? 'video/webm;codecs=vp9,opus'
  : 'video/webm';
小提示:开始录制后,记得把「开始」按钮禁用,「暂停」和「停止」按钮启用。用户体验上,这是基本操作。

2. 暂停录制:不是你想的那样

暂停录制,很多人以为就是 recorder.pause() 就完了。没错,API 确实是这样。但有个坑——暂停后,浏览器会继续接收媒体流,只是不写入文件。这意味着摄像头灯可能还亮着,麦克风还在收音。

我曾经遇到一个用户投诉,说「我明明点了暂停,为什么同事还能听到我说话?」嗯,这就是因为暂停只暂停了录制,没有暂停媒体流。如果你需要同时关闭摄像头/麦克风,得手动 stop 轨道:

recorder.pause();
// 可选:暂停媒体流
stream.getTracks().forEach(track => track.enabled = false);

恢复录制时再重新启用:

recorder.resume();
stream.getTracks().forEach(track => track.enabled = true);
注意:暂停状态下,recorder.state 会变成 'paused'。你可以利用这个状态来判断当前是否可恢复。

3. 恢复录制:简单但容易忘

恢复录制就是 recorder.resume()。没什么复杂的。但有个细节——恢复后,录制的文件是连续的,不会产生新的文件片段。这一点和「停止后再开始」完全不同。

我个人建议在 UI 上做清晰的状态切换:

  • 录制中 → 显示「暂停」按钮
  • 暂停中 → 显示「恢复」按钮
  • 停止后 → 显示「开始」按钮

这样用户一看就明白当前是什么状态,不会误操作。

4. 停止录制:别忘了释放资源

停止录制调用 recorder.stop() 即可。但这里有个关键点——停止后,一定要释放媒体流。否则摄像头灯会一直亮着,用户会以为程序出 bug 了。

recorder.stop();
stream.getTracks().forEach(track => track.stop());

另外,停止后 MediaRecorder 会触发 onstop 事件,你可以在里面做最后的文件处理:

recorder.ondataavailable = (event) => {
  if (event.data.size > 0) {
    chunks.push(event.data);
  }
};

recorder.onstop = () => {
  const blob = new Blob(chunks, { type: 'video/webm' });
  // 保存或上传 blob
};

5. 处理录制错误:别让用户一脸懵

录制过程中可能出现的错误,我总结了几种常见情况:

错误类型 原因 处理方式
NotAllowedError 用户拒绝了摄像头/麦克风权限 提示用户开启权限
NotFoundError 没有找到摄像头/麦克风设备 提示检查设备连接
SecurityError 非安全上下文(非 HTTPS 或 localhost) 提示使用 HTTPS
AbortError 录制被意外中止 尝试重新开始录制

我建议统一用一个错误处理函数:

function handleRecordingError(error) {
  console.error('录制出错:', error.name, error.message);
  // 根据 error.name 给出不同提示
  if (error.name === 'NotAllowedError') {
    alert('请允许使用摄像头和麦克风权限');
  } else if (error.name === 'NotFoundError') {
    alert('未检测到摄像头或麦克风设备');
  } else {
    alert('录制出现异常,请重试');
  }
  // 重置 UI 状态
  resetUI();
}
核心要点:错误处理不是简单的 alert,而是要让用户知道「发生了什么」以及「该怎么办」。我曾经见过一个产品,录制失败只显示「Error 1001」,用户完全看不懂。

6. 录制时长限制:防止用户录到天荒地老

时长限制这个功能,说白了就是定时器。我一般用 setTimeout 来实现:

const MAX_DURATION = 60 * 1000; // 60 秒
let timer = null;

function startRecordingWithLimit() {
  recorder.start();
  timer = setTimeout(() => {
    recorder.stop();
    alert('录制已超时,自动停止');
  }, MAX_DURATION);
}

但这里有个坑——如果用户手动暂停了,计时器还在跑。所以暂停时要清除计时器,恢复时重新设置:

recorder.onpause = () => {
  clearTimeout(timer);
};

recorder.onresume = () => {
  timer = setTimeout(() => {
    recorder.stop();
    alert('录制已超时,自动停止');
  }, MAX_DURATION - elapsedTime);
};

嗯,这里需要记录一下已经录制的时间 elapsedTime,否则恢复后计时就不准了。

知识体系总览

下面这张图,我把整个录制控制的核心逻辑画出来了。你看一眼就能明白各个状态之间的转换关系:

录制控制状态机 空闲 录制中 暂停 已停止 错误 开始录制 暂停 恢复 停止 / 超时 停止 异常 重置 空闲 录制中/错误 暂停 已停止 正常流转 异常流转

从这张图可以清楚看到,整个录制控制就是一个有限状态机。每个状态都有明确的进入条件和退出条件。我个人觉得,把状态机画出来,代码写起来就清晰多了。

总结一下

录制控制这块,说白了就是几个 API 的调用组合。但真正做好,需要考虑用户体验、错误处理、资源释放这些细节。我建议你在写代码之前,先把状态图画好,把每个边界情况想清楚。

嗯,这一章的内容就到这里。代码示例我都放在项目仓库里了,你可以直接跑起来试试。有什么问题,欢迎在评论区交流。


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