Skip to content

Instantly share code, notes, and snippets.

@Fusl
Last active January 13, 2018 21:01
Show Gist options
  • Save Fusl/acd469ee5d498eb57884101a5371d665 to your computer and use it in GitHub Desktop.
Save Fusl/acd469ee5d498eb57884101a5371d665 to your computer and use it in GitHub Desktop.
quickndirty: serving a /dev/video0 webcam via network
#!/usr/bin/env ndoe
var devicepath = '/dev/v4l/by-id/usb-Hewlett_Packard_HP_Webcam_HD_4310-video-index0';
var devicefps = 15;
var width = 1280;
var height = 720;
var bufsize = 512 * 1024 * 1024; // 512 MiB (perfect for a RPi)
var net = require('net');
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var v4l2camera = require("v4l2camera");
var exec = require('child_process').exec;
var CronJob = require('cron').CronJob;
var stream = new EventEmitter();
var preframetime = Date.now();
var framerate = 0;
var framebuffer = new Buffer(0);
var jpegheader = new Buffer([0xFF, 0xD8, 0xFF, 0xDB]);
var framecounter = 0;
var log = function () {
console.log.bind(this).apply(this, ['\r\33[2K' + new Date()].concat(Array.prototype.slice.call(arguments)));
};
var autoexposure = function (cb1, cb2) {
log('Setting exposure to automatic');
exec('v4l2-ctl -c exposure_auto=3 -d ' + devicepath, function (err, stdout, stderr) {
if (typeof cb1 === 'function') {
cb1();
}
setTimeout(function () {
log('Setting exposure to fixed');
exec('v4l2-ctl -c exposure_auto=1 -d ' + devicepath, function (err, stdout, stderr) {
if (typeof cb2 === 'functioon') {
cb2();
}
});
}, 5000);
});
};
/*new CronJob('0 0 * * * *', function () {
autoexposure();
}, null, true, 'Etc/UTC');*/
exec('v4l2-ctl -v width=' + width + ',height=' + height + ',pixelformat=MJPG -d ' + devicepath, function (err, stdout, stderr) {
exec('v4l2-ctl -p ' + devicefps + ' -d ' + devicepath, function (err, stdout, stderr) {
exec('v4l2-ctl -c focus_auto=0 -d ' + devicepath, function (err, stdout, stderr) {
exec('v4l2-ctl -c focus_absolute=0 -d ' + devicepath, function (err, stdout, stderr) {
var firstframe = true;
var cam = new v4l2camera.Camera(devicepath);
if (cam.configGet().formatName !== "MJPG") {
log('NOTICE: MJPG camera required');
process.exit(1);
}
cam.start();
autoexposure();
var getimage = function () {
cam.capture(function (success) {
if (stream.listeners('frame').length) {
process.nextTick(getimage);
} else {
setTimeout(getimage, 1000);
}
if (!success) {
return;
}
var frame = Buffer(cam.frameRaw());
if (!frame.slice(0, 4).equals(jpegheader)) {
return;
}
if (firstframe) {
//exec('v4l2-ctl -c exposure_auto=1 -d ' + devicepath);
firstframe = false;
}
var curframetime = Date.now();
stream.emit('frame', curframetime, frame);
framecounter++;
framerate = (framerate * 99 + 1000 / (curframetime - preframetime)) / 100;
preframetime = curframetime;
framebuffer = frame;
});
};
getimage();
});
});
});
});
setInterval(function () {
process.stdout.write([
'\r\33[2K' + (new Date()),
'framebuffer.length=' + framebuffer.length,
'frame.rate=' + Math.round(framerate * 100) / 100,
'listeners.length=' + stream.listeners('frame').length,
'framecounter=' + framecounter
].join(' '));
}, 500);
var tcpserver = net.createServer(function (socket) {
var addr = socket.remoteAddress;
log(addr, 'connect', 'tcp');
var stats = {total:0,sent:0,dropped:0};
var forward = function (frametime, frame) {
stats.total++;
if (socket._writableState.length < bufsize / stream.listeners('frame').length) {
stats.sent++;
socket.write(frame);
} else {
stats.dropped++;
}
};
stream.on('frame', forward);
socket.on('error', new Function());
socket.on('close', function () {
log(addr, 'disconnect', 'tcp', 'total=' + stats.total, 'sent=' + stats.sent, 'dropped=' + stats.dropped, '(' + Math.round(stats.sent / stats.total * 10000) / 100 + '%/' + Math.round(stats.dropped / stats.total * 10000) / 100 + '%)');
stream.removeListener('frame', forward);
});
});
var httpserver = http.createServer(function (req, res) {
var addr = req.socket.remoteAddress;
log(addr, 'connect', 'http');
var stats = {total:0,sent:0,dropped:0};
var forward = function (frametime, frame) {
stats.total++;
if (res._writableState.length < bufsize / stream.listeners('frame').length) {
stats.sent++;
socket.write(frame);
} else {
stats.dropped++;
}
};
stream.on('frame', forward);
req.on('error', new Function());
res.on('error', new Function());
req.on('close', function () {
log(addr, 'disconnect', 'http', 'total=' + stats.total, 'sent=' + stats.sent, 'dropped=' + stats.dropped, '(' + Math.round(stats.sent / stats.total * 10000) / 100 + '%/' + Math.round(stats.dropped / stats.total * 10000) / 100 + '%)');
});
});
var picserver = net.createServer(function (socket) {
socket.on('error', new Function());
socket.end(framebuffer);
});
tcpserver.listen(9000);
httpserver.listen(8000);
picserver.listen(7000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment