从 Firefox 49 及更高版本开始,来自 IP 摄像机的 MJPEG 流使用内部服务卡住在第一帧。这不会发生在 Chrome 中。我在 FF 49 更新日志中找不到任何可能导致此错误的更改。请记住,这段代码不是我的,而且非常旧,但它在 Chrome 中仍然可以正常工作。 我认为可能导致错误的代码片段:
CameraplayerUI.js
self.drawStream = function (image) {
//#region Argument validation and sanitization
if (typeof image === "undefined" || image === null) { return false; }
if (typeof image.src === "undefined" || image.src === null) { return false; }
if (!image.complete) { return false; }
if (_stream.width !== image.width) { _stream.width = image.width; }
if (_stream.height !== image.height) { _stream.height = image.height; }
//#region Argument validation and sanitization
if(_isLive !== true){
_isLive = true;
$(_image).hide();
$(_stream).show();
}
_ctx.drawImage(image, 0, 0, _stream.width, _stream.height);
self.source = image.src;
return true;
/** Mjpegstream.js
* Updates the current stream based on the elapsed time since last update.
* Warning: Numeric representations for all parameters are used without validation for
* performance considerations.
*
* @param {Integer} time - Current time}
* @param {Integer} elapsedTime - Elapsed time since last update cycle.
*/
this.update = function (time, elapsedTime) {
if (!self.isOpen) { return false; }
//#region Argument validation and sanitization
time = +time; // Unary plus operation. Numeric representation.
elapsedTime = +elapsedTime; // Unary plus operation. Numeric representation.
//#endregion Argument validation and sanitization
_serviceReauthenticationTimer = _serviceReauthenticationTimer - elapsedTime;
if (_serviceReauthenticationTimer <= 0) {
downloadAsync(_userId, _userKey, this.cameraId, update_callback);
}
// Firefox MJPEG stream fix.
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
if (this.data !== "undefined" && this.data !== null) {
self.data.src = _stream.src;
}
}
return true;
};
Cameraplayer.js
if (self.isLive) {
_time = now;
ui.setTime(_time);
if (!_mjpegStream.isAuthenticated) {
ui.showAuthenticationNotification(false, _mjpegStream.error);
self.hasError = true;
} else if (_mjpegStream.error !== null) {
ui.showError(true, _mjpegStream.error);
self.hasError = true;
} else if (_mjpegStream.isOpen) {
ui.clearNotifications();
if (_mjpegStream.isPlaying) {
if (_mjpegStream.update(_time, elapsedTime)) {
ui.drawStream(_mjpegStream.data);
}
} else {
_mjpegStream.play();
}
} else if (_mjpegStream.isConnecting) {
ui.showLoading(true);
return false;
} else {
ui.showLoading(true);
_mjpegStream.open(_request);
return false;
}
} else {
_time = time;
请记住,这个程序很大,我只是摘取了我认为可能会导致错误的片段。它适用于 49 之前的所有 Firefox 版本,目前适用于 Chrome
最佳答案
你依赖于 chrome bug .
根据 specs :
... when a CanvasImageSource object represents an animated image in an HTMLOrSVGImageElement, 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.
MJPEG 流是这些动画图像的一部分,因此 UA 必须仅返回第一帧。 Chrome 最近遵循了这部分规范,但仅用于 GIF 图像。
因此,通过在每次重绘时在请求中添加一个随机参数,强制完全重新加载 MJPEG 流的解决方法仍然存在,但您将失去 MJPEG 格式的所有优势并发出大量繁重的请求。
var url = 'http://webcam.st-malo.com/axis-cgi/mjpg/video.cgi?resolution=352x288';
var img = new Image();
img.onload = drawAndReload;
// check if there are already parameters in the passed url
var paramHead = url.indexOf('?') > 0 ? '&' : '?';
img.src = url;
var ctx = c.getContext('2d');
function drawAndReload(){
if(c.width !== this.naturalWidth){
c.width = this.naturalWidth;
c.height = this.naturalHeight;
}
ctx.drawImage(this, 0, 0);
// reset the image
this.src = ''; // should not be necessary
this.src = url + paramHead + Math.random(); // force no-cache
}
<canvas id="c"></canvas>
一个真正的解决方案是将您的 MJPEG 流转换为视频流服务器端,然后使用 MediaSource用于获取 block 并将其显示在 HTMLVideoElement 中的 API,您将能够不受限制地在 Canvas 上绘制。
关于javascript - 自 Firefox 49 以来,MJPEG 流在第一帧卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41291610/