• JavaScript 实现页面中录音功能


    页面中实现录音需要使用浏览器提供的 Media​Recorder API,所以前提是需要浏览器支持 MediaStream Recording 相关的功能。

    以下代码默认工作在 Chrome 环境中。

    准备页面

    首先准备一个页面,其中内容很简单,一个录音按钮,一个用于播放的 <audio> 标签。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>audio record</title>
    </head>
    <body>
        <div class="app">
            <button class="record-btn">record</button>
            <audio controls class="audio-player"></audio>
        </div>
        <script src="./recorder.js"></script>
    </body>
    </html>

    获取录音权限

    因为录音需要使用设备的话筒,所以第一步应该是向用户索要录音的权限。这是通过 Media​Devices​.get​User​Media()
    来完成的,其用法为:

    var promise = navigator.mediaDevices.getUserMedia(constraints);

    其中 constraints 为需要获取的权限列表,这里只需要指定音频 audio 即可。

    其返回是个 Promise,因为用户何时进行授权是不确定的。通过在 Promise 的回调中进行授权成功或失败的处理。

    在使用前需要判断浏览器是否已经支持相应的 API,此时得到如下的代码:

    if (navigator.mediaDevices.getUserMedia) {
      const constraints = { audio: true };
      navigator.mediaDevices.getUserMedia(constraints).then(
        stream => {
          console.log("授权成功!");
        },
        () => {
          console.error("授权失败!");
        }
      );
    } else {
      console.error("浏览器不支持 getUserMedia");
    }

    其中成功回调里得到的入参 streamMediaStream 对象。

    此时运行后可看到浏览器展示出了让用户授权使用麦克风的提示。

    向用户索要麦克风使用权限

    向用户索要麦克风使用权限

    创建录音实例

    将上一步获取到的 MediaStream 传入 Media​Recorder 的构造器创建一个录音器的实例。

    var mediaRecorder = new MediaRecorder(stream);

    启动录音

    通过监听页面中录音按钮的点击来启动录音。

    const recordBtn = document.querySelector(".record-btn");
    const mediaRecorder = new MediaRecorder(stream);
    recordBtn.onclick = () => {
      mediaRecorder.start();
      console.log("录音中...");
    };

    MediaRecorder 实例上有个 state 状态,可用来判断录音器当前的活动状态,总共有三种值:

    • inactive:处于休息状态,要么是没开始,要么是开始后已经停止。
    • recording:录音中
    • paused:已经开始,但被暂停了,不是停止也没有被恢复。

    所以通过这个状态,我们可以实现再次点击按钮时,结束录音。

    recordBtn.onclick = () => {
      if (mediaRecorder.state === "recording") {
        mediaRecorder.stop();
        recordBtn.textContent = "record";
        console.log("录音结束");
      } else {
        mediaRecorder.start();
        console.log("录音中...");
        recordBtn.textContent = "stop";
      }
      console.log("录音器状态:", mediaRecorder.state);
    };

    音频数据的获取

    上面按钮处理来自用户的交互,只负责启动或停止录音。音频的数据还是从 Media​Recorder 实例上通过监听其相应的事件来完成的。

    当录音开始时,会触发其 MediaRecorder.ondataavailable 事件,从该事件回调的入参为 BlobEvent,从它身上取到 event.data 便是我们需要的音频数据。因为数据是一段一段产生的,所以需要暂存到一个数组中。

    const chunks = [];
    mediaRecorder.ondataavailable = function(e) {
      chunks.push(e.data);
    };

    目前为止完成的代码应该是这样的:

    recorder.js
    const recordBtn = document.querySelector(".record-btn");
    const playBtn = document.querySelector(".play-btn");
    

    if (navigator.mediaDevices.getUserMedia) {
    var chunks = [];
    const constraints = { audio: true };
    navigator.mediaDevices.getUserMedia(constraints).then(
    stream => {
    console.log("授权成功!");

      <span class="pl-k">const</span> <span class="pl-c1">mediaRecorder</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">MediaRecorder</span>(stream);
    
      <span class="pl-smi">recordBtn</span>.<span class="pl-en">onclick</span> <span class="pl-k">=</span> () <span class="pl-k">=&gt;</span> {
        <span class="pl-k">if</span> (<span class="pl-smi">mediaRecorder</span>.<span class="pl-smi">state</span> <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">"</span>recording<span class="pl-pds">"</span></span>) {
          <span class="pl-smi">mediaRecorder</span>.<span class="pl-c1">stop</span>();
          <span class="pl-smi">recordBtn</span>.<span class="pl-smi">textContent</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>record<span class="pl-pds">"</span></span>;
          <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>录音结束<span class="pl-pds">"</span></span>);
        } <span class="pl-k">else</span> {
          <span class="pl-smi">mediaRecorder</span>.<span class="pl-c1">start</span>();
          <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>录音中...<span class="pl-pds">"</span></span>);
          <span class="pl-smi">recordBtn</span>.<span class="pl-smi">textContent</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>stop<span class="pl-pds">"</span></span>;
        }
        <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>录音器状态:<span class="pl-pds">"</span></span>, <span class="pl-smi">mediaRecorder</span>.<span class="pl-smi">state</span>);
      };
    
      <span class="pl-smi">mediaRecorder</span>.<span class="pl-en">ondataavailable</span> <span class="pl-k">=</span> <span class="pl-k">function</span>(<span class="pl-smi">e</span>) {
        <span class="pl-smi">chunks</span>.<span class="pl-c1">push</span>(<span class="pl-smi">e</span>.<span class="pl-c1">data</span>);
      };
    },
    () <span class="pl-k">=&gt;</span> {
      <span class="pl-en">console</span>.<span class="pl-c1">error</span>(<span class="pl-s"><span class="pl-pds">"</span>授权失败!<span class="pl-pds">"</span></span>);
    }
    

    );
    } else {
    console.error("浏览器不支持 getUserMedia");
    }

    录音状态的查看

    录音状态的查看

    录音结束及音频的播放

    通过监听 MediaRecorder.onstop 事件,将收集好的音频数据创建成 Blob 对象,然后 通过 URL.createObjectURL 创建成 HTML 中 <audio> 标签可使用的资源链接。

    mediaRecorder.onstop = e => {
      var blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" });
      chunks = [];
      var audioURL = window.URL.createObjectURL(blob);
      audio.src = audioURL;
    };

    其中,在使用完收到到的音频数据后,将 chunks 置空方便下次录音时使用。

    目前为止完成的代码应该是这样的:

    recorder.js
    const recordBtn = document.querySelector(".record-btn");
    const player = document.querySelector(".audio-player");
    

    if (navigator.mediaDevices.getUserMedia) {
    var chunks = [];
    const constraints = { audio: true };
    navigator.mediaDevices.getUserMedia(constraints).then(
    stream => {
    console.log("授权成功!");

      <span class="pl-k">const</span> <span class="pl-c1">mediaRecorder</span> <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">MediaRecorder</span>(stream);
    
      <span class="pl-smi">recordBtn</span>.<span class="pl-en">onclick</span> <span class="pl-k">=</span> () <span class="pl-k">=&gt;</span> {
        <span class="pl-k">if</span> (<span class="pl-smi">mediaRecorder</span>.<span class="pl-smi">state</span> <span class="pl-k">===</span> <span class="pl-s"><span class="pl-pds">"</span>recording<span class="pl-pds">"</span></span>) {
          <span class="pl-smi">mediaRecorder</span>.<span class="pl-c1">stop</span>();
          <span class="pl-smi">recordBtn</span>.<span class="pl-smi">textContent</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>record<span class="pl-pds">"</span></span>;
          <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>录音结束<span class="pl-pds">"</span></span>);
        } <span class="pl-k">else</span> {
          <span class="pl-smi">mediaRecorder</span>.<span class="pl-c1">start</span>();
          <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>录音中...<span class="pl-pds">"</span></span>);
          <span class="pl-smi">recordBtn</span>.<span class="pl-smi">textContent</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>stop<span class="pl-pds">"</span></span>;
        }
        <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>录音器状态:<span class="pl-pds">"</span></span>, <span class="pl-smi">mediaRecorder</span>.<span class="pl-smi">state</span>);
      };
    
      <span class="pl-smi">mediaRecorder</span>.<span class="pl-en">ondataavailable</span> <span class="pl-k">=</span> <span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> {
        <span class="pl-smi">chunks</span>.<span class="pl-c1">push</span>(<span class="pl-smi">e</span>.<span class="pl-c1">data</span>);
      };
    
      <span class="pl-smi">mediaRecorder</span>.<span class="pl-en">onstop</span> <span class="pl-k">=</span> <span class="pl-smi">e</span> <span class="pl-k">=&gt;</span> {
        <span class="pl-k">var</span> blob <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Blob</span>(chunks, { type<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>audio/ogg; codecs=opus<span class="pl-pds">"</span></span> });
        chunks <span class="pl-k">=</span> [];
        <span class="pl-k">var</span> audioURL <span class="pl-k">=</span> <span class="pl-c1">window</span>.<span class="pl-c1">URL</span>.<span class="pl-en">createObjectURL</span>(blob);
        <span class="pl-smi">player</span>.<span class="pl-smi">src</span> <span class="pl-k">=</span> audioURL;
      };
    },
    () <span class="pl-k">=&gt;</span> {
      <span class="pl-en">console</span>.<span class="pl-c1">error</span>(<span class="pl-s"><span class="pl-pds">"</span>授权失败!<span class="pl-pds">"</span></span>);
    }
    

    );
    } else {
    console.error("浏览器不支持 getUserMedia");
    }

    运行

    完成上面步骤后,实现了一个简单的录音功能,可通过此地址在线体验。完整的代码可在仓库 wayou/audio-recorder 中获取到。

    相关资源

  • 相关阅读:
    Genbank简介
    Asc码与字符互相转化
    Netbeans中文乱码
    弹出警告窗口
    PHP代码执行漏洞总结
    透析SCN
    oracle用户管理的完全恢复4:在ARCHIVELOG 模式(恢复打开的数据库数据库最初是关闭的)
    Oracle用户管理的不完全恢复2:基于取消的恢复
    RMAN备份详解1
    oracle用户管理的完全恢复6:控制文件损坏(控制文件前后内容改变)
  • 原文地址:https://www.cnblogs.com/Wayou/p/js_audio_recorder.html
Copyright © 2020-2023  润新知