Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTMLImageElement - src as stream

In the past, you could use URL.createObjectURL() and pass it a MediaStream. However, this has been removed (see https://www.fxsitecompat.dev/en-CA/docs/2017/url-createobjecturl-stream-has-been-deprecated/).

The replacement functionality was to instead use HTMLMediaElement.srcObject. This does a good job of covering the video case.

However, HTMLImageElement does not inherit from HTMLMediaElement. It does not have srcObject, either.

In my specific case, I am developing a FireFox plugin that utilizes the WebRequest filter stream functionality to do image transformations. With that API I get ArrayBuffer chunks of data. I would like to be able to stream these as I receive them to an Image() that is decoding them on the fly rather than simply accumulating them, turning them into a Blob, and then converting into a URL via URL.createObjectURL(blob).

Is there a way I can accomplish this in a streaming fashion?

(Note 1: I'm ok with a FireFox specific solution if need be.)

(Note 2: I tried setting HTMLVideoElement src to e.g. PNG but it appears that the video element is indeed picky and only supports video formats rather than stills. If I could get HTMLVideoElement.srcObject to load image stills, that might make a MediaStream-based solution possible, too).

like image 473
J Trana Avatar asked Sep 27 '19 16:09

J Trana


1 Answers

URL.createObjectURL( MediaStreamInstance ) has been deprecated, not URL.createObjectURL( BlobInstance ) nor other valid inputs for this method (like MediaSource).

MediaStreams are only able to expose video and audio media data, I couldn't find a quote were it's been said exactly, but reading the specs makes it pretty clear, along with the fact a MediaStreamTrack.kind can only be "audio" or "video".

Just like you can't set <img src="video.mp4">, you never could pass a MediaStream to an <img>, so it is not a problem for you, and URL.createObjectURL will work as you wish:

const img = document.getElementById( 'img' );
fetch( "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" )
.then( (resp) => resp.arrayBuffer() )
.then( (buf) => {
  const blob = new Blob( [buf], { type: 'image/png' } );
  img.src = URL.createObjectURL( blob );
} );
<img id="img">

The only way to have a true stream in an <img> is through the MJPEG format.


It has been decided that MediaStreams should not be allowed anymore, because too often authors didn't revoke it when they should have, and a MediaStream that got linked by a blobURI had to be kept alive, meaning that even the hardware source in case of a local stream had to be kept running for the whole life of this blobURI (which can be quite long in some circumstances).

When all major browsers finally did support the MediaElement.srcObject property for MediaStreams, that made little sense to keep this.

Here is a long discussion that occurred on the specs' repo.
Here is Firefox's bug report.
Here is Chrome's one.

(note that the only one thing that got removed is the ability to make an <iframe> or <object> point to such a MediaStream through a blobURI, but there is no real use case to use these instead of the <video> element.)

like image 164
Kaiido Avatar answered Sep 28 '22 05:09

Kaiido