【问题标题】:How to avoid SecurityError: The operation is insecure. at Firefox when using .mozCaptureStream and MediaRecorder?如何避免 SecurityError:操作不安全。在 Firefox 上使用 .mozCaptureStream 和 MediaRecorder 时?
【发布时间】:2017-09-18 05:14:37
【问题描述】:

给定代码

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <video width="320" height="280"></video>
  <script>
      const video = document.querySelector("video");
      const src = "https://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm#t=0,10";
      let recorder;
      video.oncanplay = () => {
        video.play(); 
        const mediaStream = video.captureStream();
        recorder = new MediaRecorder(mediaStream);
        recorder.ondataavailable = event => 
          console.log(URL.createObjectURL(event.data));
        
        recorder.start();
      }

      video.onpause = () => recorder && recorder.stop();

      video.src = src;
  </script>
</body>

</html>

Chromium 浏览器使用 .captureStream()MediaRecorder() 捕获 &lt;video&gt; 元素的播放,并在 MediaRecorder 实例的 dataavailable 事件中记录 Blob URL 的结果 Blob

在当前实现 .mozCaptureStream() 的 Firefox 浏览器中,抛出错误 recorder.start() 调用 SecurityError: The operation is insecure. 并在 recorder.stop() 调用 InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

这些错误显然是由于Security ConsiderationsMedia Capture from DOM Elements 造成的。

我们如何避免上述错误,并在 Firefox 浏览器上实现 Chromium 和 Chrome 浏览器相同的结果?

【问题讨论】:

  • 这实际上听起来像一个 chrome 错误!视频的crossOrigin 应设置为"anonymous" 以使您的代码不会抛出(请注意,FF 确实是has a bug about it,但至少它们 安全)。另请注意,您不能指望mozCaptureStreamcaptureStream 以相同的方式工作,这就是为什么它仍然是前缀/
  • @Kaiido Chromium 或 Chrome 没有错误。组合解决方法,同时继续研究使用MediaRecorder 创建的连接记录的解决方案。如果crossOrigin 属性也允许无错误地录制媒体播放,您能否发布一个回答来描述解决方案?
  • 正如我在链接到的 FF 错误中所说,FF 不允许 MediaElement 使用正确的 CORS 标头提供服务,因此只要不修复,它就无法作为解决方法。但我要说的是,这是 chrome 中的一个安全漏洞(我现在马上让他们处理它。)他们不应该让在没有 CORS 标头的 mediaElement 上调用 captureStream。这违背了所有其他 CORS 安全措施(例如在 webAudio 或 canvas API 中)所以无论如何你都应该在你的 mediaElement 上设置cross-origin 属性。
  • @Kaiido specification 并不完全清楚跨域请求究竟应该发生什么。您是否还在规范中提交了issue?您能否分享您打算或已提交的 Chromium 错误的链接?
  • 规格中有什么不清楚的地方?它明确指出它“必须保护其免受文档来源的访问。” 另外,this 怎么会不是错误?不,我不能链接到这个错误,因为它是一个安全问题(除非你是谷歌开发者?)

标签: javascript firefox mediastream web-mediarecorder


【解决方案1】:

您可以使用XMLHttpRequest()fetch() 获取媒体资源的Blob 表示,使用URL.createObjectURL()Blob 创建一个Blob URL

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <video width="320" height="280"></video>
  <script>
    (async() => {
      const video = document.querySelector("video");
      const src = "https://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm#t=0,10";
      const url = new URL(src);
      let recorder;
      video.oncanplay = () => {
        // note, audio is not output here
        video.play();
        const mediaStream = video.mozCaptureStream();
        recorder = new MediaRecorder(mediaStream);
        recorder.ondataavailable = event =>
          // audio is output at resulting `Blob URL`         
          console.log(URL.createObjectURL(event.data));

        recorder.start();
      }

      video.onpause = () => recorder && recorder.stop();

      const blob = await fetch(url).then(response => response.blob());
      video.src = URL.createObjectURL(blob) + url.hash;

    })();
  </script>
</body>

</html>

或者,我们可以通过将媒体资源的ArrayBuffer 表示附加到MediaSource 实例的SourceBuffer 来记录MediaSource() 的媒体播放设置为HTMLMediaElement.src

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <video width="320" height="280"></video>
  <script>
  (async() => {
  // SecurityError: The operation is insecure.
  //  InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
  const video = document.querySelector("video");
  const src = "https://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm#t=0,10";
  const url = new URL(src);
  const mediaSource = new MediaSource();
  const mimeCodec = "video/webm;codecs=opus";
  video.src = URL.createObjectURL(mediaSource);
  mediaSource.onsourceopen = async() => {
    const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
    const mediaBuffer = await fetch(url).then(response => response.arrayBuffer());
    sourceBuffer.appendBuffer(mediaBuffer);
  }
  let recorder;
  video.oncanplay = () => {
    // note, audio is not output here
    video.play(); 
    const mediaStream = video.mozCaptureStream();
    recorder = new MediaRecorder(mediaStream);
    recorder.ondataavailable = event => 
      // audio is output at resulting `Blob URL` 
      console.log(URL.createObjectURL(event.data)); 
      
    recorder.start();
  }

  video.ontimeupdate = () => {
    if (Math.floor(video.currentTime) === Number(url.hash.split(",").pop())) {
      video.ontimeupdate = null;
      video.pause();
      mediaSource.endOfStream();
    }
  }

  video.onpause = () => recorder && recorder.stop();

})();

</script>

</body>
</html>

我们还可以使用辅助函数在每个 Chromium、Chrome 浏览器和 Firefox 浏览器上使用相同的代码,例如,请参阅 Answer captureStream() on dynamically created video element

【讨论】:

    猜你喜欢
    • 2016-05-04
    • 2014-05-19
    • 2012-11-01
    • 2013-04-28
    • 2019-01-28
    • 1970-01-01
    • 2021-03-03
    • 2020-04-03
    • 1970-01-01
    相关资源
    最近更新 更多