多任务组件
本章主要介绍多任务组件。
概述
EdgerOS 每个应用程序目前只允许一个进程,不排除未来允许应用内部包含多进程。当应用需要高性能运算或并行运行时,可以在应用进程内创建多任务。
使用方法
在 人脸识别 示例 app-demo-camera-ai 项目主任务中集成了 FaceNN 模块,实现了人脸识别功能。由于人脸侦测占用较多运算资源,有时会影响到主任务的响应速度,可以使用多任务的方案解决这个问题:
- 在主任务中创建子任务,将人脸侦测功能迁移到子任务中处理。
- 主任务与子任务之间通过 Sigslot 进行通信,子任务将识别的人脸信息发送给主任务。
- 主任务接收子任务人脸信息后,分发给客户端。
参考以下示例,新建 Task 对象即为新建任务。
var task = new Task("./task.js", "test_arg_string");
如果任务间需要进行更灵活地通信时,可使用 SigSlot 信号槽,参考以下示例引入模块。
var SigSlot = require("sigslot"); var sigslot = new SigSlot("Test");
参考以下示例,在同名信号槽对象上可以在同任务或不同任务之间像事件一样注册和监听信号槽。
sigslot.slot("event1", (msg) => { // ... }); // ... sigslot.emit("event1", msg);
功能介绍
创建子任务
参考以下示例,
start()
接口实现中,当 MediaDecoder 新建对象连接 rtsp 流并启动后,创建一个子任务。start() { var netcam = new MediaDecoder(); // ... new Promise((resolve, reject) => { netcam.open(url, { proto: 'tcp', name: self.name }, 10000, (err) => { // ... }) .then((netcam) => { super.start.call(self); netcam.start(); // ... self.aiTask = new Task('./src_ai.js', { magic: 'tasks-face-flv', name: self.name, }, { directory: module.directory }); }) // ... }
创建子任务时传入了 name: self.name 选项,参考以下示例,self.name 在构造函数中定义如下。
constructor(ser, mode, inOpts, outOpts) { this.name = `${input.host}:${input.port}${input.path}`; }
主任务中新建 MediaDecoder 对象,使用
netcam.open()
打开一个命名的 rtsp 流,参考以下示例,在子任务中创建 MediaDecoder 同名副本对象。class SrcAI { constructor(name) { // ... this.start(); } start() { // ... this.netcam = new MediaDecoder().open(this.main); this.netcam.on("video", this.onVideo.bind(this)); this.netcam.start(); } // ... } var name = ARGUMENT.name; var srcAI = new SrcAI(name);
监听 netcam 的 video 事件。获取视频帧数据。
使用 Sigslot 通信
参考以下示例,引入 Sigslot 模块。
var Sigslot = require("sigslot");
参考以下示例,在主任务中创建一个命名的 Sigslot 对象。
constructor(ser, mode, inOpts, outOpts) { // ... this.aiSlot = new Sigslot(this.name); var self = this; this.aiSlot.slot('error', (type, msg) => { console.error('AI task err, ai to be close:', msg); self.aiTask.cancel(); self.aiTask = null; }, 'error'); this.aiSlot.slot('face', (type, msg) => { self.onVideo(msg); }, 'face'); }
参考以下示例,在子任务中创建一个同名的 Sigslot 对象。
constructor(name) { // ... var slot = new Sigslot(name); }
参考以下示例,子任务在处理视频帧数据并通过信号槽向主任务发送消息。
onVideo(frame) { var buf = new Buffer(frame.arrayBuffer); const view = DEF_DETEC_VIEW; var faceInfo = facenn.detect(buf, { width: view.width, height: view.height, pixelFormat: FACENN_PIXEL_FORMAT }); var ret = []; /* Empty array - clear. */ for (var i = 0; i < faceInfo.length; i++) { var info = {}; info.x0 = Math.max(faceInfo[i].x0 - 10, 0); info.x1 = Math.min(faceInfo[i].x1 + 10, view.width - 1); info.y0 = Math.max(faceInfo[i].y0 - 10, 0); info.y1 = Math.min(faceInfo[i].y1, view.height - 1); ret.push(info); } /* sigslot send to src. */ if (this.slot) { this.slot.emit('face', ret); } }
参考以下示例,主任务接收人脸侦测数据。
onVideo(infos) { const view = DEF_DETEC_VIEW; for (var i = 0; i < infos.length; i++) { var info = infos[i]; info.x0 = Math.round(info.x0 * this.mediaInfo.width / view.width); info.x1 = Math.round(info.x1 * this.mediaInfo.width / view.width); info.y0 = Math.round(info.y0 * this.mediaInfo.height / view.height); nfo.y1 = Math.round(info.y1 * this.mediaInfo.height / view.height); } var cliMgr = this.getCliMgr(); cliMgr.iter(function(cli) { cli.sendData({type: 'face'}, infos); }); }