UNPKG

node-red-node-rdk-camera

Version:

搭配RDK硬件使用摄像头的Node-RED节点(Node-RED node for using camera on a RDK hardware)

487 lines (435 loc) 16.1 kB
module.exports = function(RED) { "use strict"; var settings = RED.settings; var os = require('os'); var fs = require('fs'); var execSync = require("child_process").execSync; var spawn = require('child_process').spawn; var WebSocket = require('ws'); const mipiCameraChecker = __dirname + '/lib/sh/mipicamerachecker'; const usbCameraChecker = __dirname + '/lib/sh/usbcamerachecker'; const mipiCameraCommandSudo = __dirname + '/lib/sh/nrmipitakephotosudo'; const usbCameraCommand = __dirname + '/lib/sh/nrusbtakephoto'; const mipiStreamCommandSudo = __dirname + '/lib/sh/nrmipistreamsudo'; const usbStreamCommand = __dirname + '/lib/sh/nrusbstream'; RED.log.info('Loading rdk-camera nodes...') function getChildPids(pid, pids){ try{ var childPidBuffer = execSync('pgrep -P ' + pid); var childPid = childPidBuffer.toString().replace(/\r?\n/g, ''); pids.push(childPid); getChildPids(childPid, pids); } catch(e){ return; } return; } function RDKCameraTakePhotoNode(config) { // Create this node RED.nodes.createNode(this,config); // set parameters and save locally this.cameratype = config.cameratype; this.filemode = config.filemode; this.filename = config.filename; this.filedefpath = config.filedefpath; this.filepath = config.filepath; this.fileformat = config.fileformat; this.resolution = config.resolution; this.rotation = config.rotation; this.fliph = config.fliph; this.flipv = config.flipv; this.sharpness = config.sharpness; this.brightness = config.brightness; this.contrast = config.contrast; this.imageeffect = config.imageeffect; this.exposuremode = config.exposuremode; this.iso = config.iso; this.agcwait = config.agcwait; this.quality = config.quality; this.led = config.led; this.awb = config.awb; this.name = config.name; this.activeProcesses = {}; var node = this; var readyForAll = true; function startCamera(command, params, type){ //check camera if(type === 'usb'){ const result = execSync('v4l2-ctl --list-devices'); if(result.toString().indexOf('USB') < 0 && result.toString().indexOf('usb') < 0 && result.toString().indexOf('webcam') < 0){ node.status({fill:"red",shape:"dot",text:"rdk-camera.errors.usbCamNotFound"}); return; } } //spawn node.child = spawn(command, params); node.running = true; //child stdout on node.child.stdout.on('data', function(data){ var dataStr = data.toString(); if(dataStr.indexOf('failed') >= 0){ node.status({fill:"red",shape:"ring",text:"rdk-camera.errors.badCamera"}); } else if(dataStr.indexOf('fileready') >= 0){ var url = dataStr.split(' ')[1]; url = url.replace(/\r?\n/g, ''); node.send({ payload: url }) } else if(dataStr.indexOf('stopped') >= 0){ node.status({fill:"yellow",shape:"dot",text:"rdk-camera.status.stopped"}) } else if(dataStr.indexOf('working') >= 0){ node.status({fill:"green",shape:"dot",text:"rdk-camera.status.working"}) } }) //hint node.status({fill:"green",shape:"dot",text:"rdk-camera.status.working"}); } function startMipiCamera(params){ startCamera(mipiCameraCommandSudo, params, 'mipi'); } function startUsbCamera(params){ startCamera(usbCameraCommand, params, 'usb'); } // step 1: pick necessary variables var width = 640; var height = 480; var imagePath = os.homedir() + '/Pictures/'; var imageName = 'image.jpg'; switch (this.resolution) { case '1': width = 320 height = 240 break; case '2': width = 640 height = 480 break; case '3': width = 800 height = 600 break; case '4': width = 1024 height = 768 break; case '5': width = 1280 height = 720 break; case '6': width = 1640 height = 922 break; case '7': width = 1640 height = 1232 break; case '8': width = 1920 height = 1280 break; default: width = 640 height = 480 break; } if(this.filedefpath === '0'){ if(fs.existsSync(this.filepath)){ imagePath = this.filepath; } else{ fs.mkdirSync(this.filepath, { recursive: true}); if(fs.existsSync(this.filepath)){ imagePath = this.filepath; } else{ RED.log.warn(RED._("rdk-camera.errors.invalidPath")); node.status({fill:"grey",shape:"ring",text:"rdk-camera.errors.invalidPath"}); readyForAll = false; } } } else{ if(!fs.existsSync(imagePath)){ fs.mkdirSync(imagePath, { recursive: true}); if(!fs.existsSync(imagePath)){ RED.log.warn(RED._("rdk-camera.errors.invalidPath")); node.status({fill:"grey",shape:"ring",text:"rdk-camera.errors.invalidPath"}); readyForAll = false; } } } if(this.filemode === '1'){ if(this.filename && this.filename.trim() !== '' && this.filename.lastIndexOf('.jpg') === (this.filename.length - 4)){ imageName = this.filename; } else{ RED.log.warn(RED._("rdk-camera.errors.invalidFileName")); node.status({fill:"grey",shape:"ring",text:"rdk-camera.errors.invalidFileName"}); readyForAll = false; } } else if(this.filemode === '2'){ imageName = ''; } var params = [width, height, imagePath, imageName] // console.log('params: ', params.join(' ')) // step 2: start camera (mipi or usb) var cameraChecker = mipiCameraChecker; if(this.cameratype === '1'){ cameraChecker = usbCameraChecker; } try{ execSync(cameraChecker) } catch(e){ RED.log.warn(RED._("rdk-camera.errors.badEnv")); node.status({fill:"red",shape:"ring",text:"rdk-camera.errors.badEnv"}); readyForAll = false; } // the magic to make python print stuff immediately process.env.PYTHONUNBUFFERED = 1; if(readyForAll){ if(this.cameratype === '1'){ startUsbCamera(params) } else{ startMipiCamera(params) } node.on('input', function(msg){ if(node.child && node.running){ if(msg.payload === 'start' || msg.payload === 'stop'){ node.child.stdin.write(msg.payload+'\n'); } else{ node.child.stdin.write('save\n'); } } }) node.on('close', function(done){ node.status({}); if(node.child){ // node.child.stdin.write('close\n', () => { // node.child.kill('SIGKILL'); // node.child = null; // node.running = false; // setTimeout(function() { if (done) { done(); } }, 50); // }); node.finished = done; node.child.stdin.write('close\n'); var pids = [node.child.pid.toString()]; getChildPids(node.child.pid, pids); execSync('sudo kill -9 ' + pids.join(' ')); node.child = null; if(done) done(); } else{ if(done) done(); } }) } } function RDKCameraImageStreamNode(config) { // Create this node RED.nodes.createNode(this,config); // set parameters and save locally this.cameratype = config.cameratype; this.fps = config.fps; this.resolution = config.resolution; this.defport = config.defport; this.port = config.port; this.name = config.name; var readyForAll = true; var node = this; // step 1: pick necessary variables var width = 640; var height = 480; var fps = 20; var port = 10888; function establishWsConnection(){ setTimeout(function(){ var wsClient = new WebSocket('ws://localhost:'+port); wsClient.on('error', function(error){ RED.log.warn(error); node.status({fill:"red",shape:"ring",text:"rdk-camera.errors.badConnection"}); }) wsClient.on('open', function(){ node.wsConnection = wsClient; node.status({fill:"green",shape:"dot",text:"rdk-camera.status.working"}); }) wsClient.on('close', function(){ node.wsConnection = null; RED.log.warn('connection closed'); node.status({fill:"grey",shape:"ring",text:"rdk-camera.errors.badConnection"}); }) wsClient.on('message', function message(data){ node.send({ payload: data.toString('base64') }) }) }, 1000) } function startImageStream(command, params){ node.child = spawn(command, params); node.running = true; node.child.stdout.on('data', function(data){ var dataStr = data.toString(); if(dataStr.indexOf('failed') >= 0){ node.status({fill:"red",shape:"ring",text:"rdk-camera.errors.badCamera"}); } }) node.child.on('close', function(){ node.status({fill:"red",shape:"ring",text:"rdk-camera.errors.badCamera"}); }) establishWsConnection(); } function startMipiImageStream(params){ startImageStream(mipiStreamCommandSudo, params); } function startUsbImageStream(params){ startImageStream(usbStreamCommand, params); } switch (this.resolution) { // case '1': // width = 320 // height = 240 // break; case '2': width = 640 height = 480 break; case '3': width = 800 height = 600 break; case '4': width = 1024 height = 768 break; case '5': width = 1280 height = 720 break; case '6': width = 1640 height = 922 break; case '7': width = 1640 height = 1232 break; case '8': width = 1920 height = 1280 break; default: width = 640 height = 480 break; } switch (this.fps) { case '1': fps = 30; break; case '2': fps = 15; break; case '3': fps = 10; break; case '4': fps = 5; break; case '5': fps = 2; break; case '6': fps = 1; break; default: fps = 20; break; } try{ var intPort = parseInt(this.port); if(intPort < 3000 || intPort > 65535){ RED.log.warn(RED._("rdk-camera.errors.invalidPort")); node.status({fill:"grey",shape:"ring",text:"rdk-camera.errors.invalidPort"}); readyForAll = false; } else{ port = intPort; } } catch(e){ RED.log.warn(RED._("rdk-camera.errors.invalidPort")); node.status({fill:"grey",shape:"ring",text:"rdk-camera.errors.invalidPort"}); readyForAll = false; } // step 2: start camera (mipi or usb) var cameraChecker = mipiCameraChecker; if(this.cameratype === '1'){ cameraChecker = usbCameraChecker; } try{ execSync(cameraChecker) } catch(e){ RED.log.warn(RED._("rdk-camera.errors.badEnv")); node.status({fill:"red",shape:"ring",text:"rdk-camera.errors.badEnv"}); readyForAll = false; } // the magic to make python print stuff immediately process.env.PYTHONUNBUFFERED = 1; if(readyForAll){ var params = [width, height, fps, port] // console.log('params: ', params) if(this.cameratype === '1'){ startUsbImageStream(params) } else{ startMipiImageStream(params); } node.on('input', function(msg){ if(msg.payload === 'stop'){ if(node.wsConnection){ node.wsConnection.terminate(); node.wsConnection = null; setTimeout(function(){ node.status({fill:"yellow",shape:"dot",text:"rdk-camera.status.stopped"}); }, 50) } } else if(msg.payload === 'start'){ if(!node.wsConnection){ establishWsConnection(); } } }) node.on('close', function(done){ if(node.wsConnection){ node.wsConnection.terminate(); node.wsConnection = null; } node.status({}); if(node.child){ node.finished = done; var pids = [node.child.pid.toString()]; getChildPids(node.child.pid, pids); execSync('sudo kill -9 ' + pids.join(' ')); node.child = null; if(done) done(); } else{ if(done) done(); } }) } } RED.nodes.registerType('rdk-camera takephoto', RDKCameraTakePhotoNode); RED.nodes.registerType('rdk-camera imagestream', RDKCameraImageStreamNode); }