如何从Blob URL中检索MediaStream?

13

可以像下面的代码一样从流中使用window.URL.createObjectURL()获取URL:

这是可能的。

navigator.getUserMedia({ video: true, audio: true }, function (localMediaStream) {

    var video = document.querySelector('video');
    video.src = window.URL.createObjectURL(localMediaStream);
    video.onloadedmetadata = function (e) {
        // Do something with the video here.
    };
}, 
function (err) {
    console.log("The following error occured: " + err);
}
);

问题是我有一个类似于blob URL的链接:

blob:http%3A//localhost%3A1560/f43bed15-da6c-4ff1-b73c-5640ed94e8ee

是否有一种方法可以从中检索到MediaStream对象?


是的。如果您发布的代码无法正常工作,那么最有可能是因为您在_src_之后设置了监听器,并且请记住Blob是本地资源,因此加载比Internet资源快得多。 - Paul S.
3个回答

5

注意:

URL.createObjectURL(MediaStream)已被弃用。 请不要在代码中再使用它,否则在任何新版本的浏览器中都会抛出异常。
但是,问题的前提仍然有效。



没有内置的方法可以检索Blob URL指向的原始对象。

对于Blob,我们仍然可以获取此Blob URL,并获得原始Blob的副本

const blob = new Blob(['hello']);
const url = URL.createObjectURL(blob);

fetch(url)
  .then(r => r.blob())
  .then(async (copy) => {
    console.log('same Blobs?', copy === blob);
    const blob_arr = new Uint8Array(await new Response(blob).arrayBuffer());
    const copy_arr = new Uint8Array(await new Response(copy).arrayBuffer());
    console.log("same content?", JSON.stringify(blob_arr) === JSON.stringify(copy_arr))
    console.log(JSON.stringify(copy_arr));
  })

然而对于其他对象,这种方法行不通...

const source = new MediaSource();
const url = URL.createObjectURL(source);

fetch(url)
  .then(r => r.blob())
  .then(console.log)
  .catch(console.error);

那么唯一的方法就是跟踪您的原始对象。

为此,我们可以编写简单的包装器来更新可通过URL访问的对象字典,这些包装器围绕createObjectURLrevokeObjectURL进行:

(() => {
  // overrides URL methods to be able to retrieve the original blobs later on
  const old_create = URL.createObjectURL;
  const old_revoke = URL.revokeObjectURL;
  Object.defineProperty(URL, 'createObjectURL', {
    get: () => storeAndCreate
  });
  Object.defineProperty(URL, 'revokeObjectURL', {
    get: () => forgetAndRevoke
  });
  Object.defineProperty(URL, 'getFromObjectURL', {
    get: () => getBlob
  });
  const dict = {};

  function storeAndCreate(blob) {
    var url = old_create(blob); // let it throw if it has to
    dict[url] = blob;
    return url
  }

  function forgetAndRevoke(url) {
    old_revoke(url);
    // some checks just because it's what the question titel asks for, and well to avoid deleting bad things
    try {
      if(new URL(url).protocol === 'blob:')
        delete dict[url];
    }catch(e){} // avoided deleting some bad thing ;)
  }

  function getBlob(url) {
    return dict[url];
  }
})();

// a few example uses

// first a simple Blob
test(new Blob(['foo bar']));

// A more complicated MediaSource
test(new MediaSource());

function test(original) {
  const url = URL.createObjectURL(original);
  const retrieved = URL.getFromObjectURL(url);
  console.log('retrieved: ', retrieved);
  console.log('is same object: ', retrieved === original);
  URL.revokeObjectURL(url);
}


3

video.src不等于video.srcObject

是的它们会冲突 ;) !

video.src需要源URL

video.srcObject需要源对象(截至2019年,仅支持MediaStream 可以安全地支持,也许将来您可以直接在此处放置Blob,但现在不行...)

所以这取决于您真正想要做什么:

A) 显示当前正在录制的内容

您必须有可用的MediaStream对象(您有),只需将其放入video.srcObject中即可。

navigator.getUserMedia({ video: true, audio: true }, function (localMediaStream) {
    var video = document.querySelector('video');
    video.src = ''; // just to be sure src does not conflict with us
    video.srcObject = localMediaStream;
}

B) 显示现有视频/刚录制的视频

video.srcObject = null; // make sure srcObject is empty and does not overlay our src
video.src = window.URL.createObjectURL(THE_BLOB_OBJECT);

THE_BLOB_OBJECT - 您可以通过File API创建它,或者通常情况下,如果您有某种录音机,则假设在recorder变量中,通常会有getBlob()或类似的可用方法,例如recorder.getBlob()。我强烈建议您使用现有的录音库,但为了完整起见,还有一个官方的MediaRecorder API-https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder

因此,您只是将两个东西组合在一起,您只需要将它们分开,并确保它们不冲突 :)


你说的没错,但这并没有回答问题...在2013年(当时提出问题时),还没有 srcObject,显示 MediaStream 的唯一方法是创建一个指向该 MediaStream 的 BlobURL。虽然这种方法已经被弃用,但在那个问题发布的时候,这是正常的做法。因此问题是“如何从指向 MediaStream 的 Blob URL 中检索 MediaStream”。因为我们不能像处理 Blob 一样使用 *fetch(blobURL)*。 - Kaiido
1
如果您愿意,根据我们今天所能做到的,问题将是“如何从通过URL.createObjectURL(MediaSource_instance)生成的blobURL中检索MediaSource对象。” - Kaiido
@Kaiido 你说得对,我错过了日期,不过问题是如何获取 MediaStream 对象,而不是 MediaSource,除非你想录制视频,否则这没有多大意义。 - jave.web
我使用MediaSource作为示例,因为它是另一个非Blob对象,我们可以从中生成blobURI(就像以前可以使用MediaStream一样)。因此,它是当代的等效物。而且有很好的理由想要检索MSE,例如,能够向其附加新块。 - Kaiido
好的,但是如果你能够添加新的块,那么你不是应该已经有对象引用了吗?(只是在这里好奇:D) - jave.web

2

如果您正在使用Angular2,则可以使用平台浏览器包中提供的DOMSanitizer:

import { DomSanitizer } from '@angular/platform-browser';
constructor(
    private sanitizer: DomSanitizer) {
}

然后像下面这样使用你的流:
//your code comes here...
video.src = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(stream));

这应该只


createObjectURL即将被弃用。 - Nicolas S.Xu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接