How to retrieve a MediaStream from a Blob url?

html5-audio, html5-video, javascript

Solution

Note:

URL.createObjectURL(MediaStream) has been deprecated. Do not use it in code anymore, it will throw in any recent browsers. The premise of the question is still valid though.

There is no built in way to retrieve the original object a blob URL points to.

With Blobs, we can still fetch this blob URL and we'll get a copy of the original 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));
  })

With other objects though, this won't work...

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

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

The only way then is to keep track of your original objects.

To do so, we can come up with simple wrappers around `createObjectURL` and `revokeObjectURL` to update a dictionary of objects accessible by URL:

(() => {
  // 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);
}

Problem

It was possible to get an URL using `window.URL.createObjectURL()` from a stream like in below code. ``` 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); } ); ``` Problem is now I have a blob URL like: blob:http%3A//localhost%3A1560/f43bed15-da6c-4ff1-b73c-5640ed94e8ee Is there a way to retrieve the MediaStream object from that?

Original source