We write a script that reads a large set of JPG files on our server (endlessly, since we have another process that continues to write JPG files to the same directory) and send them to users' browsers as a MJPEG stream with a fixed time interval ( variable "frameDelay" in the code below). This is similar to what an IP camera would do.
We found that the memory usage of this script continues to grow and always ends up being killed by the system (Ubuntu);
We have checked this seemingly simple script many times. So I am posting the code below. Any comments / suggestions are welcome!
app.get('/stream', function (req, res) { res.writeHead(200, { 'Content-Type':'multipart/x-mixed-replace;boundary="' + boundary + '"', 'Transfer-Encoding':'none', 'Connection':'keep-alive', 'Expires':'Fri, 01 Jan 1990 00:00:00 GMT', 'Cache-Control':'no-cache, no-store, max-age=0, must-revalidate', 'Pragma':'no-cache' }); res.write(CRLF + "--" + boundary + CRLF); setInterval(function () { if(fileList.length<=1){ fileList = fs.readdirSync(location).sort(); }else{ var fname = fileList.shift(); if(fs.existsSync(location+fname)){ var data = fs.readFileSync(location+fname); res.write('Content-Type:image/jpeg' + CRLF + 'Content-Length: ' + data.length + CRLF + CRLF); res.write(data); res.write(CRLF + '--' + boundary + CRLF); fs.unlinkSync(location+fname); }else{ console.log("File doesn't find") } } console.log("new response:" + fname); }, frameDelay); }); app.listen(port); console.log("Server running at port " + port);
To facilitate troubleshooting, the following is an example of a standalone (non-third-party lib) test.
It has exactly the same memory problem (memory usage continues to grow and finally the OS was killed).
We believe the problem is with the setInterval () loop - maybe these images were not deleted from memory after sending or something else (maybe still stored in the variable "res"?).
Any feedback / suggestions are welcome!
var http = require('http'); var fs = require('fs'); var framedelay = 40; var port = 3200; var boundary = 'myboundary'; var CR = '\r'; var LF = '\n'; var CRLF = CR + LF; function writeHttpHeader(res) { res.writeHead(200, { 'Content-Type': 'multipart/x-mixed-replace;boundary="' + boundary + '"', 'Transfer-Encoding': 'none', 'Connection': 'keep-alive', 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT', 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', 'Pragma': 'no-cache', }); res.write(CRLF + '--' + boundary + CRLF); } function writeJpegFrame(res, filename) { fs.readFile('./videos-8081/frames/' + filename, function(err, data) { if (err) { console.log(err); } else { res.write('Content-Type:image/jpeg' + CRLF); res.write('Content-Length:' + data.length + CRLF + CRLF); res.write(data); res.write(CRLF + '--' + boundary + CRLF); console.log('Sent ' + filename); } }); } http.createServer(function(req, res) { writeHttpHeader(res) fs.readdir('./videos-8081/frames', function(err, files) { var i = -1; var sorted_files = files.sort(); setInterval(function() { if (++i >= sorted_files.length) { i = 0; } writeJpegFrame(res, sorted_files[i]); }, framedelay); }); }).listen(port); console.log('Server running at port ' + port);