如何使用JavaScript检测mjpeg视频流何时停止

8
我有一个监听mjpeg视频流的html/javascript客户端:
myImg = document.getElementById('my-image');
myImg.src = 'http://myserver.com/camera.mjpeg';

功能正常,但如果由于任何原因视频流中断,视频供应将“冻结”在上次接收到的图像上,并且我没有机会向用户显示错误。我看到了这篇文章提供了一种解决方案(创建一个沿着流运行的长时间ajax请求),但只有在某些情况下才起作用。我希望能有一种更受支持的方法,例如通过disconnect事件之类的方式。

即使是在数据接收时触发的事件也比没有好。至少这样我可以知道是否已经很久没有传送帧了。仅使用addEventListener('load')只对第一帧有效。

有什么想法吗?

更新:

根据评论,我尝试了以下几种方法,但都没有成功:

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

它会发出一个 error 事件吗?myImg.addEventListener("error", () => {...}); - zero298
@zero298 很遗憾,它并没有。 - d512
你可以尝试监听“stalled”和“suspend”事件,其中一个可能适用于你的情况。 - Joseph
1
你是否曾试过使用视频属性<video>而不是<img>?因为使用<video>时有几个回调选项。 - Hudson Moreira da Cunha
有没有一种方式可以在某种游乐场环境中自己测试这个? - Brandon McConnell
显示剩余3条评论
5个回答

2

这在一般的 mjpeg 实现中很常见,例如:

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

一个简单的解决方案是设置刷新率,并将src设置为每隔约500毫秒(或根据您的网络连接/资源更少)连续刷新连接。

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

为了防止浏览器缓存,服务器发送这些标头时会添加随机数。
或者您可以创建一个可读流,并直接将一块字节的blob读入图像源中。在此存储库中有一个强大的示例,来自另一个问题

1
在Safari中,document.readyState会从interactive变为complete。
例如,在图像加载之前添加以下内容:
  <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"

在谷歌浏览器中,readyState 状态不会停留在交互式(interactive)状态,但看起来 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)

编辑2:另一种技术--使用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.decode 是这个线程中唯一有效的解决方案,其他所有方法都是疯狂的变通方法,会破坏用户体验(因为闪烁)或者根本就是错误的。当然,这需要轮询,这并不是最好的解决方案,但至少它能够做到真正需要的事情 - 检查图像是否停止工作。谢谢! - dzek

0

这样的东西行得通吗?

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

1
我不这样认为。即使在反馈停止后,“complete”属性仍然保持为“true”,而“naturalHeight”也保持不变。 - d512

0

遇到了相同的需求,在图像加载事件上进行测试,而且成功了!

如果FFMPEG停止向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>


0

以下是Beau Bouchard的回答。

  1. setInterval计时器工作正常,但它往往会使活动客户端监听达到最大值(如果您的mjpeg流直接来自IP摄像机)。可能可以创建一个重新流转发服务器,将mjpeg服务器转发以允许更多客户端能够监听它。短轮询虽然往往会消耗大量资源。

  2. 也尝试过Restream Api。当将图像加载回img标签时,会出现抖动效果,很可能是因为块是随机传输而不是通过时间平滑传输?

  3. 最终,我使用了onload img标签事件。每当加载图像时触发此事件。然后设置一个时间间隔来检查img标签是否停止加载,以确定mjpeg流是否已停止。


3
请在您的回答中提供更多细节。目前的表达方式难以理解您的解决方案。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接