Firefox で mozCaptureStream() すると音が出なくなる


WEB ページ上で Video や Audio を扱うことができる HTMLMediaElement があり、captureStream() メソッドを使用することでメディアをキャプチャできます。

Chrome 等では実際に音をスピーカー等から再生しながらキャプチャできるのですが、Firefox ではキャプチャすると音が出なかった (内部的には再生されていてキャプチャはできる) ため、メモしておきます。

もともと Firefox では captureStream() の実装が仕様に追いついておらず、moz ベンダー接頭辞が付いていますが、MDN にはこの音が出なくなる仕様に関しては書かれていませんでした。

参考「HTMLMediaElement.captureStream() - Web API | MDN

1. 再現コード

Chrome 77 と Firefox 71 で動作確認。

(Chrome ではセキリティの制限で index.html を file スキームで開くと cross-origin data としてブロックされるため、php -S 0.0.0.0:8080 などでローカルサーバーを立てるなどしてください。)

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>mozCaptureStream() バグ</title>
    <script src="main.js"></script>
  </head>
  <body>
    <video id="video" src="video.webm" controls></video>
    <input type="button" value="mozCaptureStream()" onclick="TEST.captureStream();">
  </body>
</html>
main.js
(()=>{

    const TEST = {};

    TEST.captureStream = () => {

        const videoElement = document.getElementById('video');

        return ( 'captureStream' in videoElement ) ? videoElement.captureStream() : videoElement.mozCaptureStream();

    };

    window.TEST = TEST;

})();

これはバグとして Bugzilla に投げられていました。

参考「1178751 - Calling mozCaptureStream on an HTMLMediaElement should not destroy the AudioSink

2. 解決策

理想的なことを言えば Firefox のコードを修正すべきですが、ここでは JavaScript 側で対処します。

main.js (Firefox 対応版)
(()=>{

    const TEST = {};

    TEST.captureStream = () => {

        const videoElement = document.getElementById('video');

        let stream;

        if ( 'captureStream' in videoElement ) {
            stream = videoElement.captureStream();
        } else if ( 'mozCaptureStream' in videoElement ) {
            stream = videoElement.mozCaptureStream();
            // ★
            const audioContext = new AudioContext();
            const mediaStreamSource = audioContext.createMediaStreamSource(stream);
            mediaStreamSource.connect(audioContext.destination);
            // 
        } else {
            console.error('Unsupported: captureStream()');
        }

        return stream;

    };

    window.TEST = TEST;

})();
  1. mozCaptureStream() すると HTMLMediaElement に接続されている AudioContext で音声が再生できなくなる。
  2. 別途新しく AudioContext を作成する。
  3. キャプチャーしたストリームからオーディオソースを作成し、AudioContext.destination 1 に接続する。

キャプチャーしたストリームは、既に音量 HTMLMediaElement.volume を反映しているので、GainNode に接続するような記述は不要です。

ただし、上記の対処法では他のバグ 1443511 の影響を受けます。

参考「1443511 - A small delay occurs when the volume is changed on Soundcloud」(「Soundcloud でボリュームが変更されると、わずかな遅延が発生します」2 )


  1. スピーカーなど、最終的な音声の出力先。 

  2. SoundCloud はサイト名。バグ自体はそのサイトに限らず起きる。