0

I've been working on a solution, that would stream a series of jpg's to the client, and show them as they were a video. This is to avoid autoplay issues on iPads within a panoramic presentation. We don't need audio, so I tried looking into MJPEG, but since the browser never caches a stream, it's not a viable solution, although it performs really well on the client.

Have any of you come across something similar, and have you solved it? At this point I'm getting slightly obsessed with getting it to work!

The node.js MJPEG streaming server looks like this so far:

var express = require('express'),
    fs = require('fs'),
    http = require('http');

var app = express();

var server = http.createServer(app);

// Routes
app.use(express.static('app'));

app.get(/\.(mjpeg)$/i, function(request, res) {
    var path = res.req._parsedOriginalUrl.path,
        string = path.replace(/.*\\|\..*$/g, ''),
        name = string.replace('/', '');

    var files = fs.readdirSync('app/assets/streams/' + name + '/');

    res.writeHead(200, {
        'Content-Type': 'multipart/x-mixed-replace; boundary=myboundary',
        'Cache-Control': 'no-cache',
        'Connection': 'close',
        'Pragma': 'no-cache'
    });

    var i = 1, id;
    var stop = false;

    res.connection.on('close', function() { stop = true; });

    var send_next = function () {
        if (stop)
            return;

        i = (i + 1);

        // Increment with zero based number padding. (SO: ignore this, implementation specific)
        if (i < 100 && i >= 10)
            id = '0' + i;
        else if (i < 10)
            id = '00' + i;
        else
            id = i;

        var filename = id + '.jpg';

        //console.log(i, id, 'file length:', files.length, 'Path: app/assets/streams/' + name + '/' + filename);

        fs.readFile('app/assets/streams/' + name + '/' + filename, function (err, content) {
            res.write("--myboundary\r\n");
            res.write("Content-Type: image/jpeg\r\n");
            res.write("Content-Length: " + content.length + "\r\n");
            res.write("\r\n");
            res.write(content, 'binary');
            res.write("\r\n");

            // Start next in stream
            setTimeout(send_next, 42);
        });

        // If we're at the end, reset current stream.
        if (i === files.length) i = 1;
    };

    send_next();
});

// Setup
var port = process.env.PORT || 5000;

server.listen(port, function() {
    console.log('Listening on ' + port);
});

I'm aware there are several optimisations that can done in that code, but it's just for demonstration purposes.

Edit: I'm looking for a solution that can serve 400 frames (~20kb, potentially as base64 encoded strings), that I could then cache on the client, and stream in a continuous loop. Curious if anyone else has solved a similar problem, and how.

Kalms
  • 13
  • 1
  • 7
  • So, what was your question again? How to transfer 100MB via node.js, or how to display 400 images in 10 seconds on iPhone? Those need to be discussed independently, as they are unrelated at all. – alandarev Oct 21 '14 at 09:21
  • I don't feel like they're unrelated questions, but fair point I guess. – Kalms Oct 21 '14 at 09:29
  • One could easily build an array of images and loop through them, but what I'm looking for is how to best serve that array of images. Does that help? :) – Kalms Oct 21 '14 at 09:31
  • 1
    Standard http protocol usually expects a single file to the request, thus I suggest you use socket.io instead of traditional HTTP. Sending files over Socket can be achieved in many ways, for example using Delivery: https://github.com/liamks/Delivery.js . P.S. I have no experience in this subject, so don't take my word for granted. – alandarev Oct 21 '14 at 09:36
  • I'm actually looking into socket.io right now. Didn't know about Delivery.js, thanks! – Kalms Oct 21 '14 at 11:22

0 Answers0