高级录制功能:录制时添加时间戳、叠加文字、分段录制与自动停止

说实话,基础录制功能做出来之后,你会发现——嗯,离真正能用的产品还差得远。用户想要的是「录屏里能看到时间」、「能加个水印」、「录到一半自动分段」、「超时了自己停」。这些需求,我在好几个项目里都碰到过。

今天我们就来把这四个高级功能一个一个啃下来。我会把代码拆开讲,你跟着敲一遍,基本就能掌握。

1. 录制时添加时间戳

时间戳说白了就是在视频画面上叠加一个实时时钟。我习惯用 canvas 来做这件事,而不是直接操作视频流。

为什么?因为 canvas 可以灵活绘制文字,而且不影响原始视频轨道。

核心思路:把摄像头流和 canvas 流合并成一个新的 MediaStream。

来看代码:

// 创建 canvas,尺寸和视频一致
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

// 假设 video 是 <video> 元素
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;

// 每帧绘制:先画视频,再画时间戳
function drawTimestamp() {
  ctx.drawImage(video, 0, 0);
  ctx.font = '24px monospace';
  ctx.fillStyle = 'white';
  ctx.shadowColor = 'black';
  ctx.shadowBlur = 4;
  const now = new Date();
  const timeStr = now.toLocaleTimeString('zh-CN', { hour12: false });
  ctx.fillText(timeStr, 16, 40);
  requestAnimationFrame(drawTimestamp);
}

// 从 canvas 获取流
const canvasStream = canvas.captureStream(30); // 30fps

小技巧:我建议用 shadowBlur 给文字加个阴影,这样不管背景是亮是暗,时间戳都看得清。我曾经踩过这个坑——白字配白背景,录完啥也看不见。

2. 录制时叠加文字信息

叠加文字和时间戳本质一样,但文字内容可能是动态的,比如用户名、直播间号、或者自定义标语。

我个人习惯把文字信息封装成一个对象,方便管理:

const overlayText = {
  content: '蓝海资料掘金营',
  x: 16,
  y: canvas.height - 32,
  fontSize: '20px',
  color: 'rgba(255, 255, 255, 0.8)'
};

function drawOverlay() {
  ctx.drawImage(video, 0, 0);
  // 先画时间戳
  drawTimestamp();
  // 再画叠加文字
  ctx.font = `${overlayText.fontSize} sans-serif`;
  ctx.fillStyle = overlayText.color;
  ctx.shadowBlur = 4;
  ctx.fillText(overlayText.content, overlayText.x, overlayText.y);
  requestAnimationFrame(drawOverlay);
}

你想想看,如果用户想改文字内容,只需要更新 overlayText.content 就行,不需要动绘制逻辑。这就是解耦的好处。

注意:canvas 绘制是 CPU 密集操作。如果帧率太高(比如 60fps),低端设备可能会发热。我一般用 30fps,画质和性能平衡得不错。

3. 实现分段录制

分段录制,说白了就是录到一定时长自动切文件。比如每 10 分钟存一个片段。

实现方式有两种:

  • 方式一:监听 MediaRecorder.ondataavailable,定时生成 Blob。
  • 方式二:手动停止再启动一个新的 Recorder。

我推荐方式一,更平滑,不会丢帧。来看代码:

const CHUNK_DURATION = 600000; // 10分钟,单位毫秒
let startTime = Date.now();
const segments = [];

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

// 定时检查是否该分段
setInterval(() => {
  const elapsed = Date.now() - startTime;
  if (elapsed >= CHUNK_DURATION) {
    recorder.stop();
    // 保存当前段
    saveSegment(segments);
    // 清空并重新开始
    segments.length = 0;
    startTime = Date.now();
    recorder.start(1000); // 每秒触发 dataavailable
  }
}, 1000);

避坑指南:我曾经在分段时忘记重置 startTime,结果第二段录了不到 1 秒就切了。嗯,调试了半小时才发现。所以记得每次分段后都要重置计时。

4. 录制自动停止功能

自动停止通常有两种场景:

  • 录制时长达到上限(比如最长 1 小时)
  • 检测到静音或无人画面

先说时长限制。这个最简单:

const MAX_DURATION = 3600000; // 1小时

const stopTimer = setTimeout(() => {
  if (recorder.state === 'recording') {
    recorder.stop();
    console.log('录制已自动停止(达到时长上限)');
  }
}, MAX_DURATION);

再说静音检测。这个稍微复杂一点,需要用到 AnalyserNode

const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
const source = audioContext.createMediaStreamSource(stream);
source.connect(analyser);

const dataArray = new Uint8Array(analyser.frequencyBinCount);

function checkSilence() {
  analyser.getByteFrequencyData(dataArray);
  const average = dataArray.reduce((a, b) => a + b) / dataArray.length;
  if (average < 10) { // 阈值,可根据环境调整
    console.log('检测到静音,准备停止录制');
    recorder.stop();
  }
}

// 每 5 秒检测一次
setInterval(checkSilence, 5000);

注意:静音检测的阈值很敏感。我在项目里遇到过用户环境本身就很安静,结果录了 30 秒就被自动停了。建议把阈值做成可配置的参数,让用户自己调。

知识体系总览

下面这张图把四个功能的关系画清楚了。你可以看到,时间戳和文字叠加共用 canvas 绘制层,分段录制和自动停止共用定时器逻辑。

高级录制功能 时间戳叠加 文字信息叠加 分段录制 自动停止 canvas + requestAnimationFrame setInterval + dataavailable AnalyserNode 静音检测 四个功能可独立使用,也可组合使用

组合使用时的注意事项

这四个功能不是孤立的。在实际项目中,它们经常一起上。我列几个常见的坑:

功能组合 潜在问题 解决方案
时间戳 + 分段录制 分段后时间戳不连续 在每段开始时记录基准时间,绘制时显示相对时间
文字叠加 + 自动停止 静音检测误判,文字内容不变 静音检测只影响录制,不影响 canvas 绘制
分段录制 + 自动停止 停止时正好在分段点,可能丢数据 onstop 事件中强制保存当前段

我的经验:分段录制和自动停止同时开启时,一定要在 recorder.onstop 里做一次最终保存。否则最后一段可能没来得及触发 dataavailable 就没了。这个 bug 我修了整整一个下午。

好了,这四个高级功能讲完了。你可以在自己的项目里挑着用,也可以全上。代码量不大,但每个功能都能让录制体验提升一个档次。

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