17、媒体录制:MediaRecorder API、录制格式选择、分段录制与下载

聊到WebRTC,大家第一反应都是实时通信。但有个功能经常被忽略——媒体录制。说白了,就是把音视频流存成文件。

我在做第一个WebRTC项目时,客户要求“通话过程必须能回放”。当时我第一反应是架个流媒体服务器做录制。后来发现,浏览器自带的MediaRecorder就能搞定大部分场景。嗯,这里要注意,它和服务器端录制是两码事,但很多场景下够用了。

MediaRecorder API 基础

MediaRecorder是浏览器原生接口。你给它一个MediaStream,它就能把数据编码成文件。我个人习惯把它理解成“浏览器里的录像机”。

基本用法很简单:

// 获取媒体流
const stream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
});

// 创建录制器
const recorder = new MediaRecorder(stream, {
  mimeType: 'video/webm;codecs=vp9,opus'
});

// 收集数据块
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

这里有个坑:start()的参数是timeslice,单位毫秒。如果不传,ondataavailable只在stop()时触发一次。传了的话,会按时间切片触发。我曾经因为没传这个参数,导致录制大文件时内存爆了——所有数据攒到一块才处理,你想想看,录个半小时的视频,那数据量有多大。

录制格式选择

格式选择是个头疼的问题。浏览器支持的格式有限,而且各平台不一样。

格式 容器 编码 兼容性
video/webm WebM VP8/VP9 + Opus Chrome、Firefox、Edge
video/mp4 MP4 H.264 + AAC Safari、部分Chrome
audio/webm WebM Opus 所有现代浏览器

怎么选?我的经验是:先检测浏览器支持哪些格式。

function getSupportedMimeType() {
  const types = [
    'video/webm;codecs=vp9,opus',
    'video/webm;codecs=vp8,opus',
    'video/webm;codecs=h264,opus',
    'video/mp4;codecs=h264,aac',
    'audio/webm;codecs=opus'
  ];
  for (const type of types) {
    if (MediaRecorder.isTypeSupported(type)) {
      return type;
    }
  }
  return ''; // 都不支持就默认
}

说白了,VP9画质好但编码慢,VP8兼容性好,H.264在Safari上必须。我一般优先选VP9,如果浏览器不支持就降级到VP8。

注意:MediaRecorder不支持设置码率、帧率等编码参数。它用的是浏览器默认配置。如果你需要精细控制编码参数,得用WebCodecs API,那是另一个话题了。

分段录制

为什么要分段录制?两个原因:一是防止单文件过大,二是实现“回滚录制”——只保留最近N分钟的内容。

分段录制的核心逻辑:监听ondataavailable,每收到一段数据就保存成一个独立文件。

class SegmentedRecorder {
  constructor(stream, options = {}) {
    this.stream = stream;
    this.segmentDuration = options.segmentDuration || 10000; // 每段10秒
    this.maxSegments = options.maxSegments || 0; // 0表示不限
    this.segments = [];
    this.recorder = null;
    this.isRecording = false;
  }

  start() {
    this.isRecording = true;
    this._createRecorder();
  }

  _createRecorder() {
    const recorder = new MediaRecorder(this.stream, {
      mimeType: getSupportedMimeType()
    });

    recorder.ondataavailable = (e) => {
      if (e.data.size > 0) {
        this.segments.push(e.data);
        // 如果限制了最大段数,删除最旧的
        if (this.maxSegments > 0 && this.segments.length > this.maxSegments) {
          this.segments.shift();
        }
      }
    };

    recorder.onstop = () => {
      if (this.isRecording) {
        this._createRecorder(); // 继续下一段
      }
    };

    recorder.start(this.segmentDuration);
    this.recorder = recorder;
  }

  stop() {
    this.isRecording = false;
    if (this.recorder && this.recorder.state !== 'inactive') {
      this.recorder.stop();
    }
  }

  getBlob() {
    return new Blob(this.segments, { type: 'video/webm' });
  }
}

这个模式有个好处:你可以实现“只保留最近30秒”的循环录制。我在做安防监控项目时就用过这个思路——摄像头一直录,但只保留最近5分钟的数据,超出就丢弃。这样既不会爆内存,又能保证随时有回放可用。

小技巧:分段录制时,每段的timeslice不要设得太小。我建议至少1秒,否则会产生大量小文件,合并时也麻烦。10秒一段是比较平衡的选择。

下载与保存

录制完成后怎么下载?最直接的方式是用<a>标签触发下载。

function downloadBlob(blob, filename) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename || `recording_${Date.now()}.webm`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url); // 释放内存
}

如果你录了多个分段,想合并成一个文件再下载,可以这样:

async function mergeAndDownload(segments, filename) {
  const blob = new Blob(segments, { type: 'video/webm' });
  downloadBlob(blob, filename);
}

注意:WebM格式本身支持拼接,只要编码一致,直接合并Blob就能播放。但MP4不行,MP4需要重新封装。所以如果你用MP4格式,分段录制后合并会比较麻烦,得借助mp4box.js之类的库。

核心要点:
  • MediaRecorder接收MediaStream,输出Blob
  • 格式优先选WebM(VP9/VP8 + Opus),兼容性最好
  • 分段录制用timeslice参数,ondataavailable回调处理每段
  • 下载用createObjectURL + a标签点击
  • 合并分段时,WebM可以直接拼,MP4需要额外处理

知识体系图

媒体录制知识体系 MediaRecorder API 输入:MediaStream 输出:Blob 文件 格式选择 WebM(VP9/VP8) / MP4(H.264) 分段录制 timeslice参数 + ondataavailable 下载与保存 createObjectURL + a标签 循环录制(回滚模式) 分段合并下载 格式兼容性检测 核心流程:获取流 → 选择格式 → 分段录制 → 合并下载

这张图把整个媒体录制的流程串起来了。从输入流开始,经过格式选择、分段录制,最后下载保存。底部三个应用场景是我在实际项目中经常用到的。

最后说一句:MediaRecorder虽然方便,但功能有限。如果你需要更精细的控制——比如指定编码参数、做视频裁剪、加滤镜——那就得用WebCodecs + Canvas的组合了。不过那是进阶内容,先把MediaRecorder玩熟再说。

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