You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JavaScript在移动端Safari及全浏览器实现音频录制

适配移动端浏览器的音频录制方案

我明白你遇到的糟心问题——桌面Chrome跑的好好的录制代码,到了iPhone的Safari/Chrome就直接罢工,这主要是移动端浏览器在媒体API支持、权限要求和编码格式上有不少差异。下面我给你梳理问题点,再给出修改后的兼容代码:

核心问题分析

  1. 用户交互强制要求:iOS浏览器(包括Safari和Chrome)严格要求媒体权限请求(getUserMedia)必须由用户主动触发(比如点击按钮),原代码页面加载时就直接调用,会被浏览器无情拒绝权限。
  2. MediaRecorder兼容性:iOS Safari在iOS 14.5之后才支持标准MediaRecorder,而且只支持部分音频编码格式,原代码用的audio/ogg在iOS上完全不被支持。
  3. API前缀兼容:部分旧版移动端浏览器可能需要webkit前缀的API(比如navigator.webkitMediaDevices)。

修改后的兼容代码

'use strict';

const log = console.log.bind(console);
const id = val => document.getElementById(val);
const ul = id('ul');
const gUMbtn = id('gUMbtn');
const start = id('start');
const stop = id('stop');
let stream, recorder, counter = 1, chunks;

// 适配不同浏览器支持的音频格式,优先选iOS支持的audio/mp4(AAC编码)
const getSupportedAudioFormat = () => {
  if (MediaRecorder.isTypeSupported('audio/mp4')) {
    return { tag: 'audio', type: 'audio/mp4', ext: '.m4a' };
  } else if (MediaRecorder.isTypeSupported('audio/wav')) {
    return { tag: 'audio', type: 'audio/wav', ext: '.wav' };
  } else {
    return { tag: 'audio', type: 'audio/ogg', ext: '.ogg' };
  }
};

let media = getSupportedAudioFormat();

// 初始化权限请求按钮事件(必须由用户点击触发,符合iOS要求)
gUMbtn.onclick = async () => {
  try {
    // 兼容不同浏览器的媒体设备API
    const mediaDevices = navigator.mediaDevices || navigator.webkitMediaDevices;
    if (!mediaDevices) {
      alert('当前浏览器不支持音频录制功能');
      return;
    }

    // 请求音频权限
    stream = await mediaDevices.getUserMedia({ audio: true });
    
    // 显示控制按钮
    id('btns').style.display = 'inherit';
    start.removeAttribute('disabled');
    gUMbtn.disabled = true;

    // 初始化MediaRecorder,使用兼容的格式
    recorder = new MediaRecorder(stream, { mimeType: media.type });
    
    recorder.ondataavailable = (e) => {
      chunks.push(e.data);
      if (recorder.state === 'inactive') {
        const blob = new Blob(chunks, { type: media.type });
        const url = URL.createObjectURL(blob);
        // 创建预览和下载链接
        makeLink(url);
        // 上传录制的音频
        uploadAudio(blob);
      }
    };

    recorder.onstop = () => {
      // 停止录制后释放媒体流,避免资源占用
      stream.getTracks().forEach(track => track.stop());
    };

  } catch (err) {
    log('获取媒体权限失败:', err);
    alert('无法获取音频权限,请检查浏览器设置');
  }
};

start.onclick = () => {
  start.disabled = true;
  stop.removeAttribute('disabled');
  chunks = [];
  recorder.start();
};

stop.onclick = () => {
  stop.disabled = true;
  recorder.stop();
  start.removeAttribute('disabled');
};

// 创建预览和下载元素
function makeLink(url) {
  const li = document.createElement('li');
  const mt = document.createElement(media.tag);
  const hf = document.createElement('a');

  mt.controls = true;
  mt.src = url;
  hf.href = url;
  hf.download = `${counter++}${media.ext}`;
  hf.textContent = `${hf.download}`;

  li.appendChild(mt);
  li.appendChild(hf);
  ul.appendChild(li);
}

// 上传音频的函数,优化了错误处理
async function uploadAudio(blob) {
  try {
    const reader = new FileReader();
    reader.onload = () => {
      const url = "http://localhost:8080/rest/api/v1/audio/submit";
      const payload = JSON.stringify({ recording: reader.result });
      const xhttp = new XMLHttpRequest();
      xhttp.open("POST", url, true);
      xhttp.setRequestHeader('Content-Type', 'application/json');
      xhttp.onload = () => {
        if (xhttp.status >= 200 && xhttp.status < 300) {
          log('音频上传成功');
        } else {
          log('音频上传失败:', xhttp.statusText);
        }
      };
      xhttp.onerror = () => log('上传请求出错');
      xhttp.send(payload);
    };
    reader.readAsDataURL(blob);
  } catch (err) {
    log('上传处理失败:', err);
  }
}

关键修改点说明

  • 用户交互触发权限请求:把getUserMedia的调用放到了gUMbtn的点击事件里,完美符合iOS浏览器的权限要求。
  • 动态选择支持的音频格式:通过MediaRecorder.isTypeSupported检测浏览器支持的格式,优先选择iOS支持的audio/mp4,确保录制的音频能在移动端正常播放和处理。
  • API前缀兼容:增加了对navigator.webkitMediaDevices的判断,适配旧版移动端浏览器。
  • 媒体流释放:在录制停止后主动停止媒体轨道,避免不必要的资源占用。
  • 错误处理优化:增加了更详细的错误提示和捕获,方便调试问题。
  • 代码结构优化:把上传逻辑抽成独立函数,让代码更清晰易维护。

额外注意事项

  • iOS上的Chrome是基于Safari内核的,所以它的媒体API限制和Safari完全一致,不需要单独区分。
  • 确保你的页面是HTTPS协议(localhost除外),因为现代浏览器要求媒体API必须在安全上下文下运行。
  • 如果需要兼容iOS 14.5以下的旧版本,可能需要使用第三方库(比如recorder.js)做降级处理,因为这些版本不支持标准MediaRecorder。

内容的提问来源于stack exchange,提问作者M.Mendix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:48:03