Firefox 安全错误:在分配 MediaSource 的 blob url 时,http://localhost:NNNN/ 上的内容可能无法从 blob:http://localhost:NNNN/... 加载数据

问题描述 投票:0回答:1

我目前有一个设置,当不同浏览器不支持某些功能时,可以模拟 Web Workers 支持,以便在所有浏览器上使用相同的消息系统。在这种情况下,Firefox 不支持 Web Worker 中的媒体源扩展。 当处于“无工作人员”模式时,我创建一个 MessageChannel 并使用 MessageChannel 端口在“工作人员”线程和主线程之间交换消息。这就是我的代码的整体样子:

// on port1 of the MessageChannel (representing the worker side)
postMessageToMain("createVideo")
const mediaSource = new MediaSource()
if (workerSupported){
  const handle = mediaSource.handle
  postMessageToMain("setSource", handle, [handle])
} else {
  const handle = URL.createObjectURL(mediaSource)
  postMessageToMain("setSource", handle)
}

// on port2 of the MessageChannel (representing the main thread)
onmessage(m){
  switch (m) {
    case "createVideo":{
        videoElement = document.createElement('video')
        videoElement.playsInline = true
        break
    }
    case "setSource":{
        if(workerSupported){
         videoElement.srcObject = m.data.handle // it is of type MediaSourceHandle
        } else {
         videoElement.src = m.data.handle // is of type string, here the error in the title is thrown
        }
    }
  }
}

此代码在 Chrome 上以工作模式和非工作模式运行,而在 safari 上运行,因为不支持 Web Worker 中的 MSE,因此仅在非工作模式下工作。 我没有设置任何 CSP 标头。 难道我做错了什么?我在 SO 中发现了同样的问题,但我很难找到实际的问题及其解决方案

最小可重现示例:https://stackblitz.com/edit/vitejs-vite-hxbktr?file=main.js

(在 Firefox 上打开并检查控制台)

firefox content-security-policy web-worker media-source
1个回答
1
投票

非常奇怪的错误,当后者没有在创建的同一任务中使用时,它们似乎不保留 MediaSource 和 blob: URL 之间的链接。这是一个实际的最小示例:

const video = document.querySelector('video');
const mediaSource = new MediaSource();
const url = URL.createObjectURL(mediaSource);
setTimeout(() => {
  video.src = url;
});
<video controls></video>

同步设置 src 可以避免该错误

const video = document.querySelector('video');
const mediaSource = new MediaSource();
const url = URL.createObjectURL(mediaSource);
video.src = url;
<video controls></video>

从异步回调中创建 blob: URL 也是如此:

const video = document.querySelector('video');
const mediaSource = new MediaSource();
setTimeout(() => {
  video.src = URL.createObjectURL(mediaSource);
});
<video controls></video>

因此,对于您的情况,您可能希望使用同步操作来模拟 Worker 上下文:

// assuming you don't use addEventListener()
const fakeMain = { postMessage(data) { fakeWorker.onmessage?.({ data }); } };
const fakeWorker = { postMessage(data) { fakeMain.onmessage?.({ data }); } };

/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

// This code adapted from Eric Bidelman's demo at
// http://html5-demos.appspot.com/static/media-source.html

var FILE = 'https://upload.wikimedia.org/wikipedia/commons/a/a4/BBH_gravitational_lensing_of_gw150914.webm';
var NUM_CHUNKS = 5;
var video = document.querySelector('video');

if (!window.MediaSource) {
  alert('The MediaSource API is not available on this platform');
}

const fakeMain = { postMessage(data) { fakeWorker.onmessage?.({ data }); } };
const fakeWorker = { postMessage(data) { fakeMain.onmessage?.({ data }); } };

fakeWorker.onmessage = (message) => {
  console.log('received message', message);
  switch (message.data.type) {
    case 'init': {
      console.log('received message in worker');
      const mediaSource = new MediaSource();
      mediaSource.addEventListener(
        'sourceopen',
        function () {
          var sourceBuffer = mediaSource.addSourceBuffer(
            'video/webm; codecs="vorbis,vp8"'
          );
          console.log(sourceBuffer);

          log('MediaSource readyState: ' + this.readyState);

          get(FILE, function (uInt8Array) {
            var file = new Blob([uInt8Array], {
              type: 'video/webm',
            });
            var chunkSize = Math.ceil(file.size / NUM_CHUNKS);

            log('Number of chunks: ' + NUM_CHUNKS);
            log('Chunk size: ' + chunkSize + ', total size: ' + file.size);

            // Slice the video into NUM_CHUNKS and append each to the media element.
            var i = 0;

            (function readChunk_(i) {
              // eslint-disable-line no-shadow
              var reader = new FileReader();

              // Reads aren't guaranteed to finish in the same order they're started in,
              // so we need to read + append the next chunk after the previous reader
              // is done (onload is fired).
              reader.onload = function (e) {
                sourceBuffer.appendBuffer(new Uint8Array(e.target.result));
                log('Appending chunk: ' + i);
                if (i === NUM_CHUNKS - 1) {
                  sourceBuffer.addEventListener('updateend', function () {
                    if (
                      !sourceBuffer.updating &&
                      mediaSource.readyState === 'open'
                    ) {
                      mediaSource.endOfStream();
                    }
                  });
                } else {
                  if (video.paused) {
                    video.play(); // Start playing after 1st chunk is appended.
                  }
                  readChunk_(++i);
                }
              };

              var startByte = chunkSize * i;
              var chunk = file.slice(startByte, startByte + chunkSize);

              reader.readAsArrayBuffer(chunk);
            })(i); // Start the recursive call by self calling.
          });
        },
        false
      );

      mediaSource.addEventListener(
        'sourceended',
        function () {
          log('MediaSource readyState: ' + this.readyState);
        },
        false
      );
      const url = window.URL.createObjectURL(mediaSource);
      fakeWorker.postMessage({ type: 'setSrc', url });
    }
  }
};

fakeMain.onmessage = (message) => {
  switch (message.data.type) {
    case 'setSrc': {
      console.log('received message in main');

      video.src = message.data.url;
    }
  }
};

document.querySelector('[data-num-chunks]').textContent = NUM_CHUNKS;
fakeMain.postMessage({ type: 'init' });
function get(url, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.responseType = 'arraybuffer';
  xhr.send();

  xhr.onload = function () {
    if (xhr.status !== 200) {
      alert('Unexpected status code ' + xhr.status + ' for ' + url);
      return false;
    }
    callback(new Uint8Array(xhr.response));
  };
}

function log(message) {
  document.getElementById('data').innerHTML += message + '<br /><br />';
}
<video controls></video>
<div data-num-chunks></div>
<div id="data"></div>

我看到你确实打开了https://bugzil.la/1906040希望这能得到一些关注并尽快得到修复。

© www.soinside.com 2019 - 2024. All rights reserved.