将本地 mjpg 视频流式传输到 html canvas
Stream local mjpg video to html canvas
我正在尝试将 mjpg 视频的实时流写入 html canvas。
以下内容:http://camelive.info/ 有一个包含 mjpeg 视频的 public 网络摄像头列表,但它们似乎正在编写带有框架元素的
许多现有的 IP mjpeg 摄像机实际上以预定义的帧速率发送单独的 jpeg 文件,这些文件在经常更新时看起来像视频。
您需要检查您的相机制造商 API 以获取正确的 url 以获取图像流,例如使用 Foscam 相机我之前已经完成了以下操作并且效果很好:
<img id='videostream' src="http://123.456.789.233:8080/videostream.cgi">
您显然必须获得正确的摄像机 IP 和端口号(如果存在)。
更新 - 这并不意味着您不能使用其他实时视频流方法,这只是我所知道的从 IP 摄像机获取实时视频的最简单方法。
UPDATE 2 - 还有一些相机有用户名和密码,因此您可能必须将它们附加到 url videostream.cgi?user=your_user&password=your_password
希望对您有所帮助。
根据 to specs 关于 CanvasRenderingContext2D drawImage
方法,
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.
这适用于 .gif、SMIL 动画 .svg 和 .mjpeg 媒体。因此,一旦您获取了数据,就应该只在 canvas.
上绘制一帧
请注意 chrome has a bug 并且只尊重 .gif 图片,但他们可能有一天会修复它。
您自己注意到的一个解决方案是使用清除缓存 hack ('your.url/?' + new Date().getTime();
) 获取另一个新帧,但您将失去 mjpeg 格式(部分帧内容)的任何优势,并且可以确定何时刷新。
因此,如果适用,更好的解决方案是使用视频格式。视频的每一帧都可以绘制到canvas.
编辑 2018
两年后,第三个解决方案浮现在我的脑海中:
UA 不依赖于在内存中为文档中的所有 2DContext 保留相同的默认图像。
虽然对于其他格式我们仍然有点卡住,但对于 MJPEG 流,它没有明确定义的 默认图像 ,我们实际上落到了 动画的第一帧。
因此,通过在不同的时间在两个不同的 canvas 上绘制包含我们的 MJPEG 流的 <img>
,理论上我们可以在 canvases.
这是仅在 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>
因此,虽然此解决方案显然会带来性能影响(我们正在创建一个全新的 canvas 元素及其每一帧的 2DContext),但它仍然可能比淹没网络更好。无论如何,所有这些都应该很容易被垃圾收集。
我正在尝试将 mjpg 视频的实时流写入 html canvas。
以下内容:http://camelive.info/ 有一个包含 mjpeg 视频的 public 网络摄像头列表,但它们似乎正在编写带有框架元素的
许多现有的 IP mjpeg 摄像机实际上以预定义的帧速率发送单独的 jpeg 文件,这些文件在经常更新时看起来像视频。
您需要检查您的相机制造商 API 以获取正确的 url 以获取图像流,例如使用 Foscam 相机我之前已经完成了以下操作并且效果很好:
<img id='videostream' src="http://123.456.789.233:8080/videostream.cgi">
您显然必须获得正确的摄像机 IP 和端口号(如果存在)。
更新 - 这并不意味着您不能使用其他实时视频流方法,这只是我所知道的从 IP 摄像机获取实时视频的最简单方法。
UPDATE 2 - 还有一些相机有用户名和密码,因此您可能必须将它们附加到 url videostream.cgi?user=your_user&password=your_password
希望对您有所帮助。
根据 to specs 关于 CanvasRenderingContext2D drawImage
方法,
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.
这适用于 .gif、SMIL 动画 .svg 和 .mjpeg 媒体。因此,一旦您获取了数据,就应该只在 canvas.
上绘制一帧请注意 chrome has a bug 并且只尊重 .gif 图片,但他们可能有一天会修复它。
您自己注意到的一个解决方案是使用清除缓存 hack ('your.url/?' + new Date().getTime();
) 获取另一个新帧,但您将失去 mjpeg 格式(部分帧内容)的任何优势,并且可以确定何时刷新。
因此,如果适用,更好的解决方案是使用视频格式。视频的每一帧都可以绘制到canvas.
编辑 2018
两年后,第三个解决方案浮现在我的脑海中:
UA 不依赖于在内存中为文档中的所有 2DContext 保留相同的默认图像。
虽然对于其他格式我们仍然有点卡住,但对于 MJPEG 流,它没有明确定义的 默认图像 ,我们实际上落到了 动画的第一帧。
因此,通过在不同的时间在两个不同的 canvas 上绘制包含我们的 MJPEG 流的 <img>
,理论上我们可以在 canvases.
这是仅在 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>
因此,虽然此解决方案显然会带来性能影响(我们正在创建一个全新的 canvas 元素及其每一帧的 2DContext),但它仍然可能比淹没网络更好。无论如何,所有这些都应该很容易被垃圾收集。