🎙️

audio-recorder-polyfillを使ってiOS対応のWebブラウザ録音機能

2021/03/14に公開
1

やりたいこと

  • ブラウザで録音機能を作りたい

問題

  1. MediaRecorderを使いたいが、調査(2021 年 2 月)時点では iOS Safari は未対応だった・・・。
     - MediaRecorder 以外にも方法はありそうだったが、一番シンプルであり、今後を考えるとこれを使えるように polyfill した方がよさそうだった。
  2. 純粋に MediaRecorder を使用した場合、react の状態管理とぶつかる。audio はそれ自体が内部的に状態を持っており、react の state と二重管理になった場合に片方の状態をロストすることが起きる。
    👇 参考
    https://qiita.com/kazumicho/items/086864a9d78121d54aa7

背景

  • React(Next.js)に導入する

今回つかうもの

Setup polyfill

  1. yarn add audio-recorder-polyfill
  2. ファイル追加: public/polyfill.js
import AudioRecorder from "audio-recorder-polyfill";
window.MediaRecorder = AudioRecorder;
  1. ファイル修正: _app.tsx に以下のスクリプトを追加
      ...
      <Head>
        <script>
          {
            () => {
              // CSRでscriptを実行するために行う
              if (!window.MediaRecorder) {
                document.write(
                  decodeURI('%3Cscript defer src="/polyfill.js">%3C/script>')
                )
            }
          }
        }
        </script>
      </Head>
      ...

Install react-media-recorder

このライブラリを使うことで MediaRecorder の内部状態と react の状態管理を両立させる。

yarn add react-media-recorder

hooks が提供されているので、次のように宣言できる。

const {
  status,
  startRecording,
  stopRecording,
  resumeRecording,
  pauseRecording,
  mediaBlobUrl,
} = useReactMediaRecorder({ audio: true });

Fin: Example

実装イメージは次のような形です。

export default function Home() {
  const {
    status,
    startRecording,
    stopRecording,
    resumeRecording,
    pauseRecording,
    mediaBlobUrl,
  } = useReactMediaRecorder({ audio: true });

  const [isNowRecording, setIsNowRecording] = useState(false);

  function onStart() {
    startRecording();
    setIsNowRecording(true);
  }
  function onPause() {
    pauseRecording();
  }
  function onResume() {
    resumeRecording();
  }
  function onStop() {
    stopRecording();
    setIsNowRecording(false);
  }

  return (
    <div className={styles.container}>
      <Head>
        <script>
          {() => {
            // CSRでscriptを実行するために行う
            if (!window.MediaRecorder) {
              document.write(
                decodeURI('%3Cscript defer src="/polyfill.js">%3C/script>')
              );
            }
          }}
        </script>

        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>

        <p className={styles.description}>
          Get started by editing{" "}
          <code className={styles.code}>pages/index.js</code>
        </p>

        <div className={styles.grid}>
          <div className={styles.card}>
            <button onClick={onStart} type="button" className="m-1">
              Record
            </button>
            <button onClick={onPause} type="button" className="m-1">
              Pause
            </button>
            <button onClick={onResume} type="button" className="m-1">
              Resume
            </button>
            <button onClick={onStop} type="button" className="m-1">
              Stop
            </button>
          </div>
          <div className={styles.card}>
            <audio src={mediaBlobUrl} controls />
          </div>

          <div className={styles.card}>
            {isNowRecording ? <p>録音中</p> : <p>録音可能</p>}
          </div>
        </div>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{" "}
          <img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
        </a>
      </footer>
    </div>
  );
}

Github にリポジトリ公開しているのと、Vercel にデプロイしているので参考に iOS で開いてみてください。

https://github.com/nitaking/react-voice-recorder-sample

https://react-voice-recorder-sample-git-main-nitaking.vercel.app/

Discussion