Stream local mjpg video to html canvas
According to specs about the CanvasRenderingContext2D drawImage
method,
Specifically, when a CanvasImageSource object represents an animated
image in an HTMLImageElement, the user agent must use the default
image of the animation (the one that the format defines is to be used
when animation is not supported or is disabled), or, if there is no
such image, the first frame of the animation, when rendering the image
for CanvasRenderingContext2D APIs.
This applies to .gif, SMIL animated .svg and .mjpeg media. So once you fetched the data, only one frame should be drawn onto the canvas.
Note that chrome has a bug and only respect it for .gif images, but they may fix it someday.
One solution as you noticed yourself, is to fetch an other fresh frame, with the clear-cache hack ('your.url/?' + new Date().getTime();
) but you will loose any advantages of the mjpeg format (partial frame content) and can't be sure when the refreshing will happen.
So a better solution if applicable, would be to use a video format. Each frame of a video can be drawn to the canvas.
Edit 2018
A third solution came to my little mind two years later:
UAs are not tied to keep in memory the same default image for all 2DContexts in the document.
While for others format we are still kinda stuck, for MJPEG streams, which don't have a well defined default image, we actually fall to the first frame of the animation.
So by drawing the <img>
containing our MJPEG stream on two different canvases, at different times, we can theoretically have two different frames of our same MJPEG stream to be drawn on the canvases.
Here is a proof of concept only tested on Firefox 62.
var ctx_stream = stream.getContext('2d');var ctx_direct = direct.getContext('2d');img.onload = function() { stream.width = direct.width = this.naturalWidth; stream.height = direct.height = this.naturalHeight; // onload should fire multiple times // but it seems it's not at every frames // so we'll disable t and use an interval instead this.onload = null; setInterval(draw, 500);};function draw() { // create a *new* 2DContext var ctx_off = stream.cloneNode().getContext('2d'); ctx_off.drawImage(img, 0,0); // and draw it back to our visible one ctx_stream.drawImage(ctx_off.canvas, 0,0); // draw the img directly on 'direct' ctx_direct.drawImage(img, 0,0);} img.src = "http://webcam.st-malo.com/axis-cgi/mjpg/video.cgi?resolution=704x576&dummy=1491717369754";
canvas,img{ max-height: 75vh;}
Using a new offcreen canvas every frame: <br><canvas id="stream"></canvas><br>The original image: <br><img id="img"><br>Drawing directly the <img> (if this works your browser doesn't follow the specs): <br><canvas id="direct"></canvas><br>
Include a MJPEG in a html-Site, using video.js
You can use an <video>
tag
<video src="http://172.17.52.161:9000/stream/video.mjpeg" controls>
Your browser does not support the <code>video</code> element.
</video>
<img> with mjpeg-stream created with JS don't work fine in Firefox and Opera
Well, it seems to be fixed with using<iframe src='path_to_mjpeg'>
instead of<img src='path_to_mjpeg'>
Chrome and Opera handle its creation with JS correctly.
Firefox starts playing successfully to, but with huge memory leak.
How can I render 6 or more mjpeg streams on the same page?
Create subdomains for the camera proxies. These subdomains can all point to the same server.
Basically, this would be the setup:
NAME TYPE VALUE
--------------------------------------------------
camproxy.example.com. A 127.0.0.1
cam1.example.com. CNAME camproxy.example.com.
cam2.example.com. CNAME camproxy.example.com.
cam3.example.com. CNAME camproxy.example.com.
cam4.example.com. CNAME camproxy.example.com.
...
Then, in your control page, either use every subdomain only once or keep a track of how many times you've used one. For example:
function getCamUrl($file) {
static $subdomain = 1;
static $uses = 0;
$uses++;
if($uses > 6) {
$uses = 0;
$subdomain++;
}
return "cam" . $subdomain . ".example.com/" . $file;
}
echo '<img src="'. getCamUrl('video1.mjpeg') .'">';
echo '<img src="'. getCamUrl('video2.mjpeg') .'">';
echo '<img src="'. getCamUrl('video3.mjpeg') .'">';
echo '<img src="'. getCamUrl('video4.mjpeg') .'">';
According to the spec, browsers should only use 2 connections. You can get away with setting it to 6, this works in most modern browsers.
How to show an extermal livestream on a webpage
Yes, you can show streaming video in a video element, provided you're using a separate javascript library to demux the incoming stream (Some examples are hls.js and flv.js ). Really only iOS devices support live streaming in a native <video>
element by itself (HLS video).
You can sort out what kind of stream is incoming by using ffprobe
or maybe by looking at the Content-Type
of the http request you're making. application/octet-stream
is a generic content type, so I wouldn't be certain you're actually grabbing the real content in that request.
Lastly, you'll likely be dealing with CORS/mixed-content issues for anything live streaming (unless you're loading a mjpeg stream...which you aren't if you've got H.264 video). You'll need to get that sorted out.
How to detect when mjpeg stream stops with javascript
This is common with a normal implementation of a mjpeg
, for example
<video src="http://myserver.com/camera.mjpeg" controls>
Your browser does not support the <code>video</code> element.
</video>
the mjpeg is a series of images and eventually it will not get the next one for whatever reason, breaking the connection. (this is sometimes because the source is cached, causing the browser to use the last image every time). I don't consider this an error, more something to program around with mjpeg streams.
A simple solution you can do, set a refresh rate and set the src continuously refreshing the connection every ~500ms (or less depending on your network connection/resources).
setInterval(function() {
var myImg = document.getElementById('myImg');
myImg.src = 'http://myserver.com/camera.mjpeg?rand=' + Math.random();
}, 5000);
The random number is added to prevent browser side caching in the event the server sends those headers.
Or you can create a ReadableStream, and keep reading a blob of bytes directly into the source of the image. There is a robust example in this repo, from this other question.
Visualize mjpeg-over-http streams in browser with html5
iOs mobile Safari supports MJPEG natively over http. Is there specific reason you need it in Canvas?
see http://bridgecam2.halton.gov.uk/mjpg/video.mjpg?camera=1 on a ipad/iphone
Related Topics
Javascript Innerhtml Not Updating Element
Rendering Mjpeg Stream in Html5
Make a Clickable Link With Onclick But Without Href=#
Eslint: Disable Warning - 'Defined But Never Used' for Specific Function
Make Redirect to New Url But Remove Previous from History
How to Print Image from Browser Using JavaScript
How to Assign Empty Value to Select Dropdown
Background-Image in React Component
Assigning Id to All Elements of a Page in Angular Automatically
How to Delete Specific Item from Localstorage
Using JavaScript Node.Js How to Parallel Process for Loops
Rendering a Select Tag on Click - React Js
Discord Bot Not Getting All Users
How to Press Enter Key Automatically Using Jquery
Datatables - Search in Multiple Columns With a Single Drop Down
How to Reset Child Elements' State
Reactjs: Expected Onclick Listener to Be a Function, Instead Got Type String