如何检测 mjpeg 流何时停止 javascript

How to detect when mjpeg stream stops with javascript

我有一个正在收听 mjpeg 视频流的 html/javascript 客户端:

myImg = document.getElementById('my-image');
myImg.src = 'http://myserver.com/camera.mjpeg';

工作正常,但如果视频流由于某种原因终止,视频源将“冻结”在最后接收到的图像上,我没有机会向用户显示错误。我看到 this post 提供的解决方案(在流旁边创建一个长 运行 ajax 请求)仅在某些时候有效。我希望有一个更受支持的方法,比如通过 disconnect 事件或其他方法。

即使接收到数据的事件也总比没有好。至少这样我可以判断帧通过是否已经有一段时间了。使用 addEventListener('load') 仅适用于第一帧。

有什么想法吗?

更新:

根据评论,我尝试了以下方法,none 有效:

myImg.addEventListener('error', event => { ... });
myImg.addEventListener('stalled', event => { ... });
myImg.addEventListener('suspend', event => { ... });

这样的东西行得通吗?

function hasLoaded(myImg) {
  return myImg.complete && myImg.naturalHeight !== 0;
}

这在 mjpeg 的正常实现中很常见,例如

   <video src="http://myserver.com/camera.mjpeg" controls>
      Your browser does not support the <code>video</code> element.
   </video>

mjpeg 是一系列图像,最终无论出于何种原因它都不会获取下一个图像,从而断开连接。 (这有时是因为源被缓存,导致浏览器每次都使用上一张图片)。我不认为这是一个错误,更多的东西可以用 mjpeg 流来编程。

你可以做一个简单的解决方案,设置刷新率并设置 src 每 ~500 毫秒(或更短时间取决于你的网络 connection/resources)连续刷新连接。

setInterval(function() {
    var myImg = document.getElementById('myImg');
    myImg.src = 'http://myserver.com/camera.mjpeg?rand=' + Math.random();
}, 5000);

添加随机数是为了防止在服务器发送 headers.

时浏览器端缓存

或者您可以创建 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

正在关注 Beau Bouchard 的回答。

  1. setInterval 计时器工作正常,但它往往会最大化主动客户端侦听。 (如果您的 mjpeg 流直接来自网络摄像机)。可能会在 mjpeg 服务器上创建一个重新流式传输服务器,以允许更多客户端能够收听它)短池虽然往往会占用大量资源。

  2. 也尝试了重新播放 Api。将图像加载回 img 标签时,确实会出现抖动效果,这很可能是因为块是随机进入的,并且不会随着时间的推移而平滑?

  3. 最后,我使用了onload img标签事件。每当加载 img 时都会触发。然后一个时间间隔检查img标签是否停止加载来判断mjpeg流是否停止。

在 Safari 中 document.readyState 将从交互式变为完整。

例如,在图像加载之前放置:

  <script>
    console.log('Initial ready state', document.readyState);
    document.onreadystatechange = function() {
      console.log('Ready state changed to:', document.readyState);
    }
  </script>

输出将是:

Initial ready state – "loading"
Ready state changed to: – "interactive"

// When the connection disconnects:
Ready state changed to: – "complete"

在 google chrome 中,readyState 不会保持交互状态,但看起来 chrome 更擅长重新连接,因此对您来说可能不是问题。

编辑:利用它的一种方法是将图像放在 iframe 中,您将在 safari 中不断获得加载事件(这在 chrome 中不起作用)。

iframe = document.createElement('iframe')
iframe.onload = console.log
iframe.src = "http://10.0.0.119:8080/stream"
document.body.append(iframe)

Edit2:另一种技术 -- 使用 image.decode 检测连接何时断开并重新加载图像:

    <img id="stream" src="http://10.0.0.119:8080/stream"></img>
    <script>
      let image = document.getElementById('stream');

      async function check() {
        while (true) {
          try {
            await image.decode();
          } catch {
            let src = image.src;
            image.src = "";
            image.src = src;
          }
      
          await new Promise((resolve) => setTimeout(resolve, 5000));
        }
      }
      
      check();
    </script>

满足同样的要求,测试Image onload事件和工作!!

如果 FFMPEG feed FFSERVER 停止,虽然 MJPEG 在静止图像中,

数次错误计数后,mjpeg FAIL! 检测到。

<!DOCTYPE HTML>
<HTML>
 <HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <TITLE> mjpeg detect </TITLE>
<script type="text/javascript" language="JavaScript" src="/js/jquery.js"></script>
<script type="text/javascript" language="JavaScript">
//------------------------------------------------------------------------------
// localhost/tool/mjpeg.htm
// document.ready
$(function(){
    setTimeout("mjpegRefresh()", 10000);
});

var mTmjpegRefresh, mBmjpegStatus=0, mNmjpegError=0;
var mjpegRefresh = function()
{
    clearTimeout(mTmjpegRefresh);
    mBmjpegStatus=0;
    $('#myMJPEG').attr('src', "http://192.168.1.17:8090/live.mjpeg?rand=" + Math.random()); 
    console.log("mjpeg refresh: ", Math.round( (new Date()).getTime()/1000)) ;
    mTmjpegRefresh = setTimeout("mjpegRefresh()", 10000);
    mTmjpegStatusCheck = setTimeout("mjpegStatusCheck()", 5000);
}

var mjpegOnload = function()
{
    console.log("mjpeg Onload");
    mBmjpegStatus=1;
}

var mTmjpegStatusCheck;
var mjpegStatusCheck = function()
{
    clearTimeout(mTmjpegStatusCheck);
    if(mBmjpegStatus>0)
    {
        mNmjpegError=0;
    }
    else
    {
        mNmjpegError++;
    }
    if(mNmjpegError>5)
    {
        console.log("mjpeg FAIL!");
    }
    mTmjpegStatusCheck = setTimeout("mjpegStatusCheck()", 5000);
}
//------------------------------------------------------------------------------
</script>
 </HEAD>

 <BODY>

<img src="http://192.168.1.17:8090/live.mjpeg" width="720" height="404" id="myMJPEG" onload="mjpegOnload()">

 </BODY>
</HTML>