2

I am showing an MJPEG stream from an IP camera on a web page. The stream is shown using an image element, which is set by jQuery:

view = $('<img>');

view.load(function() {
     console.log('loaded');
});
view.error(function() {
     console.log('error');
});

view.attr('src', 'http://camera_ip/videostream.mjpeg');

Both events fire neatly when their respective situation occurs. Until I disconnect the camera. The image freezes (of course). I want to detect this disconnection, to show the user an error message. I thought up a solution, which was copying frames several seconds apart from the image to a canvas, and comparing the contents.

Is there an easier option?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Supergrover
  • 74
  • 1
  • 6

2 Answers2

1

The only way I can think of doing this on the front end is creating an AJAX request exactly when you set the src attribute of the image. The AJAX request should call the "complete" callback when the mjpeg stream ends.

If you are comfortable with node.js and/or websockets, you could alternatively set up an mjpeg proxy back end that serves up the mjpeg stream and emits a 'close' event to that client over a websocket when the stream ends. So it would look something like this (keep in mind, I still haven't figured out exactly how bufferToJPEG would parse out the single jpeg frame from the stream):

http.get('http://camera_ip/videostream.mjpeg', function(response) {
    var buffer = "";

    response.on('data', function(chunk) {
        buffer += chunk;
        clientSocket.emit('imageFrame', bufferToJPEG(buffer));
    });
    response.on('end', function() {
        clientSocket.emit('imageEnd');
    });
});

The problem with this (which I am trying to deal with in my own project right now) is that you then have to either associate a websocket with each image request, or emit the raw jpegs from the mjpeg stream as they come in over websockets (you can render those images with data uris on the front end).

Hope that helped a little -- sorry you had to wait so long for a response.

edit: https://github.com/wilhelmbot/Paparazzo.js looks like a good way of proxying that image in the way that I described above.

zigzackattack
  • 343
  • 5
  • 11
0

Here is what I came up with after reading zigzackattack answer. I use the "datauri" package for simplicity but for more fine grained control over the final image I also successfully tested the "node-canvas" package instead.

var mjpeg2jpegs = require('mjpeg2jpegs')
const Datauri = require('datauri')

var camURL = '/videostream.cgi?user=admin&pwd=password'
var camPort = 81
var camTimeout = 10000
var FPS_DIVIDER = 1

var options = {
  hostname: '192.168.1.241',
  port: camPort,
  path: camURL,
  timeout: camTimeout
}

function startCamStream (camName, options) {
  var http = require('http')
  var req = http.request(options, mjpeg2jpegs(function (res) {
    var data
    var pos = 0
    var count = 0
    res.on('imageHeader', function (header) {
    // console.log('Image header: ', header)
      data = new Buffer(parseInt(header['content-length'], 10))
      pos = 0
    })
    res.on('imageData', function (chunk) {
    // console.log('Image data: ', data.length)
      chunk.copy(data, pos)
      pos += chunk.length
    })
    res.on('imageEnd', function () {
    // console.log('Image end')
      if (count++ % FPS_DIVIDER === 0) {
        const datauri = new Datauri()
        datauri.format('.jpeg', data)
        socket.emit(camName, datauri.content) // Send the image uri via websockets. 
      }
    })
  })).on('timeout', function () {
    console.log('timeout')
    startCamStream(camName, options)
  }).end()
}

startCamStream('ipcam1', options)

Using vue.js (optional) I simply embed the image uri with an img tag.

<img :src="ipcam1"  alt="ipcam1" />

Increasing the FPS_DIVIDER variable will reduce the fps output. If you want to change the image when there is a timeout then you can send an "offline" image when it reach the "timeout" callback.

01BTC10
  • 506
  • 3
  • 9
  • 19