Reputation: 131
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?
Upvotes: 13
Views: 21480
Reputation: 136707
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);
}
Upvotes: 5
Reputation: 15032
And yes they will conflict ;) !
video.src
takes source URL
video.srcObject
takes source OBJECT (currently as of 2019 only MediaStream
is safely supported, maybe in the future you could put the Blob directly here, but not now...)
You must have MediaStream
object available (which you do) and just put it into 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;
}
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
- you either already have one created through File API, or usually if you have some kind of recorder, let's assume in recorder
variable, usually there is getBlob()
or something similar available like recorder.getBlob()
I strongly recommend you use some existing recorder library for this, but to be complete there is an official MediaRecorder API - https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
So you see you've just combined 2 things together, you just need to separate them and make sure they don't conflict :)
Upvotes: 3
Reputation: 81
In case you are using angular2, you can use the DOMSanitizer provided in the platform-browser-package:
import { DomSanitizer } from '@angular/platform-browser';
constructor(
private sanitizer: DomSanitizer) {
}
and then use your stream like the following:
//your code comes here...
video.src = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(stream));
This should only
Upvotes: 2