ntv-wangjian 3 роки тому
джерело
коміт
ab1273d1a5
6 змінених файлів з 263 додано та 40 видалено
  1. 52
    5
      GB28181Server.js
  2. 56
    1
      GB28181Session.js
  3. 132
    0
      NtvAuthModule.js
  4. 12
    32
      package.json
  5. 11
    1
      stream/server.js
  6. 0
    1
      stream/session.js

+ 52
- 5
GB28181Server.js Переглянути файл

@@ -3,7 +3,7 @@ const SIP = require('./sip/sip');
3 3
 const context = require('./core/ctx');
4 4
 const Logger = require('./core/logger');
5 5
 const NodeSipSession = require('./GB28181Session');
6
-
6
+const NtvAuthModule = require('./NtvAuthModule');
7 7
 //GB28181 SIP服务器
8 8
 class NodeSIPServer {
9 9
     constructor(config) {
@@ -18,6 +18,9 @@ class NodeSIPServer {
18 18
         this.userinfo = {};
19 19
         //会话
20 20
         this.dialogs = {};
21
+
22
+        //ntv  认证
23
+        this.authModule = new NtvAuthModule(config.GB28181.authServer);
21 24
     }
22 25
 
23 26
     run() {
@@ -52,7 +55,7 @@ class NodeSIPServer {
52 55
 
53 56
         //注册&注销 请求
54 57
         context.nodeEvent.on('register', (request, remote) => {
55
-            this.auth(request, remote);
58
+            this.ntv_auth(request, remote);
56 59
         });
57 60
 
58 61
         //处理消息
@@ -76,8 +79,52 @@ class NodeSIPServer {
76 79
         this.uas.destroy();
77 80
     }
78 81
 
82
+    ntv_auth(request, remote){
83
+        let serverInfo = SIP.parseUri(request.uri);
84
+
85
+        if (!request.headers || !request.headers.to || !request.headers.from || !serverInfo.user) {
86
+            this.uas.send(SIP.makeResponse(request, 400, 'missing to header.'));
87
+            return;
88
+        }
89
+        else if (!request.headers.to.uri) {
90
+            this.uas.send(SIP.makeResponse(request, 400, 'missing username on to header.'));
91
+            return;
92
+        }
93
+        else if (!request.headers.contact || request.headers.contact.length == 0) {
94
+            this.uas.send(SIP.makeResponse(request, 400, 'missing contact header.'));
95
+            return;
96
+        }
97
+
98
+        let toInfo = SIP.parseUri(request.headers.to.uri);
99
+
100
+        let fromInfo = SIP.parseUri(request.headers.from.uri);
101
+
102
+        //用户 标识
103
+        let userId = fromInfo.user;
104
+
105
+        Logger.log(`[${userId}] register protocol=${remote.protocol} ip=${remote.address} port=${remote.port} `);
106
+
107
+        //安全性检查
108
+        if (toInfo.user.length != 20 || fromInfo.user.length != 20 || toInfo.user != fromInfo.user/* || serverInfo.user != this.id*/) {
109
+            Logger.log(`[${userId}] check fail. `);
110
+            return;
111
+        }
112
+
113
+        let that = this;
114
+        //TODO ntv 在这里对接接口
115
+        this.authModule.auth(userId,function (data){
116
+            if(data.code==0){
117
+                let auto_play = "";
118
+                if("err_desc" in data) auto_play = data.err_desc;
119
+                that.auth(request, remote,auto_play);
120
+            }else{
121
+                Logger.log(`[${userId}] 业务层认证失败:${data.err_desc} `);
122
+            }
123
+        });
124
+    }
125
+
79 126
     //身份验证
80
-    auth(request, remote) {
127
+    auth(request, remote,auto_play) {
81 128
 
82 129
         let serverInfo = SIP.parseUri(request.uri);
83 130
 
@@ -104,7 +151,7 @@ class NodeSIPServer {
104 151
         Logger.log(`[${userId}] register protocol=${remote.protocol} ip=${remote.address} port=${remote.port} `);
105 152
 
106 153
         //安全性检查
107
-        if (toInfo.user.length != 20 || fromInfo.user.length != 20 || toInfo.user != fromInfo.user || serverInfo.user != this.id) {
154
+        if (toInfo.user.length != 20 || fromInfo.user.length != 20 || toInfo.user != fromInfo.user /*|| serverInfo.user != this.id*/) {
108 155
             Logger.log(`[${userId}] check fail. `);
109 156
             return;
110 157
         }
@@ -177,7 +224,7 @@ class NodeSIPServer {
177 224
                 //新的设备加入
178 225
                 let session = new NodeSipSession(this.config, userId, { request: request, info: remote }, this.uas);
179 226
                 session.expires = expires || this.expires;
180
-                session.run();
227
+                session.run(this.authModule,auto_play);
181 228
             }
182 229
         }
183 230
         else {

+ 56
- 1
GB28181Session.js Переглянути файл

@@ -3,9 +3,13 @@ const SIP = require('./sip/sip');
3 3
 const SDP = require('./sdp/parser');
4 4
 const Logger = require('./core/logger');
5 5
 const context = require('./core/ctx');
6
+const NtvAuthModule = require('./NtvAuthModule');
6 7
 
7 8
 class NodeSipSession {
8 9
     constructor(config, userid, remote, uas) {
10
+        
11
+        //ntv add
12
+        this.config  = config;
9 13
 
10 14
         this.request = remote.request;
11 15
 
@@ -72,7 +76,7 @@ class NodeSipSession {
72 76
     }
73 77
 
74 78
     //启动
75
-    async run() {
79
+    async run(authModule,auto_play) {
76 80
 
77 81
         this.pingInterval = setInterval(() => {
78 82
 
@@ -103,6 +107,32 @@ class NodeSipSession {
103 107
 
104 108
         //获取设备目录
105 109
         this.catalog = await this.getCatalog();
110
+
111
+        //ntv 提交状态,自动开启视频... 
112
+        this.authModule = authModule;
113
+        this.auto_play  = auto_play;
114
+        authModule.status(this.id,1,(data)=>{
115
+            if(data.code==0){
116
+                Logger.log("设备在线状态已上报!");
117
+            }else{
118
+                Logger.log("设备在线状态已上报失败!" + data.err_desc);
119
+            }
120
+            
121
+        });
122
+        
123
+        await this.startRealPlay(this.id,auto_play);
124
+    }
125
+
126
+    //ntv add 自动发realplay消息 TODO streamServer配置项增加服务器ip选项,使摄像头可以推送到其他服务器。
127
+    async startRealPlay(channelId,auto_play) {
128
+        let config = this.config;
129
+        if(config.GB28181.streamServer.enable && (auto_play || config.GB28181.streamServer.auto_play)){
130
+            let host = config.GB28181.sipServer.host;
131
+            let port = config.GB28181.streamServer.listen;
132
+            let mode = 1;
133
+            Logger.log(`[${channelId}] auto send real play message`);
134
+            this.sendRealPlayMessage(channelId,host,port,mode);
135
+        }
106 136
     }
107 137
 
108 138
     // 停止
@@ -116,6 +146,15 @@ class NodeSipSession {
116 146
 
117 147
             this.isStarting = false;
118 148
             context.sessions.delete(this.id);
149
+
150
+            this.authModule.status(this.id,0,(data)=>{
151
+                if(data.code==0){
152
+                    Logger.log("设备离线状态已上报!");
153
+                }else{
154
+                    Logger.log("设备离线状态已上报失败!" + data.err_desc);
155
+                }
156
+                
157
+            });
119 158
         }
120 159
     }
121 160
 
@@ -855,6 +894,14 @@ class NodeSipSession {
855 894
 
856 895
                                     //ntv
857 896
                                     that.playmode='realplay';
897
+                                    this.authModule.play_status(this.id,1,(data)=>{
898
+                                        if(data.code==0){
899
+                                            Logger.log("视频开启状态已上报!");
900
+                                        }else{
901
+                                            Logger.log("视频开启状态已上报失败!" + data.err_desc);
902
+                                        }
903
+                                        
904
+                                    });
858 905
 
859 906
                                     resolve(result);
860 907
                                 }
@@ -891,6 +938,14 @@ class NodeSipSession {
891 938
                             */
892 939
                             //context.nodeEvent.emit('stopPlayed', session.ssrc);
893 940
                             delete this.dialogs[key];
941
+                            this.authModule.play_status(this.id,0,(data)=>{
942
+                                if(data.code==0){
943
+                                    Logger.log("视频关闭状态已上报!");
944
+                                }else{
945
+                                    Logger.log("视频关闭状态已上报失败!" + data.err_desc);
946
+                                }
947
+                                
948
+                            });
894 949
                         }
895 950
                     });
896 951
 

+ 132
- 0
NtvAuthModule.js Переглянути файл

@@ -0,0 +1,132 @@
1
+/**
2
+ * 信令认证模块,负责对连接信令的用户进行认证和状态记录
3
+ * 终端---->信令(加载本模块)---->业务系统
4
+ * Created by WJ on 2018-12-06
5
+ * @云视睿博  www.ruiboyun.com
6
+ */
7
+
8
+const http = require('http');
9
+const qs   = require('querystring');
10
+
11
+class NtvAuthModule {
12
+	
13
+	constructor(config) {
14
+		this.config = config;
15
+	}
16
+
17
+	errorMsg(error){
18
+		return {code:5,err_desc:error};
19
+	}
20
+
21
+	/**
22
+	 * 解析接口返回内容
23
+	 * 并回调,回调传输人的第二个参数是一个对象,格式同接口返回格式。
24
+	 * @param {*} data 
25
+	 * @param {*} callback 参数为json对象,结构同ntv g3接口规范
26
+	 */
27
+	parseData(data,callback) {
28
+		if(typeof(callback)!="function"){
29
+			console.log("auth module needs callback function");
30
+			return;
31
+		}
32
+		var obj = null;
33
+		try {
34
+			obj  = JSON.parse(data);
35
+			if(!"code" in obj){
36
+				obj.code = 5;
37
+				obj.err_desc = "接口返回数据内容不可理解!";
38
+			}
39
+		} catch(error) {
40
+			obj = this.errorMsg("解析接口返回数据失败!")
41
+		}
42
+
43
+		callback(obj);
44
+	}
45
+
46
+	/**
47
+	 * 请求远程接口
48
+	 * @param {接口的action} action 
49
+	 * @param {参数} data  参数键值对 对象或字符串
50
+	 * @param {回调} callback 
51
+	 */
52
+	postCall(host,port,path,data,callback) {
53
+		var returnData = '';
54
+		var content    = '';
55
+		if(typeof(data)=="object"){
56
+			content = qs.stringify(data);
57
+		}else{
58
+			content = data;
59
+		}
60
+
61
+		var options = {
62
+			hostname: host,
63
+			port: port,
64
+			path: path,
65
+			method: 'POST',
66
+			timeout: 2000,
67
+			headers: {
68
+				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
69
+			}
70
+		};
71
+		var that = this;
72
+		var req = http.request(options, function(res) {
73
+			res.setEncoding('utf8');
74
+			res.on('data', function(chunk) {
75
+				returnData = returnData + chunk;
76
+			});
77
+			res.on('end', () => {
78
+				that.parseData(returnData,callback);
79
+			});
80
+		});
81
+
82
+		req.on('error', function(e) {
83
+			if(typeof(callback)=="function"){
84
+				that.callback(that.errorMsg(5,'无法连接业务服务器!'));
85
+			}
86
+		});
87
+
88
+		req.write(content);
89
+		req.end();
90
+	}
91
+
92
+	auth(usrId,callback){
93
+		var config = this.config;
94
+		var path = "/api/sipMgr/";
95
+		var paras= "action=sip_auth&id=" + usrId;
96
+		var host = config.host;
97
+		var port = config.port;
98
+		this.postCall(host,port,path,paras,callback);
99
+	}
100
+
101
+	/**
102
+	 * 
103
+	 * @param {*} usrId 
104
+	 * @param {*} status  1 = online
105
+	 * @param {*} callback 
106
+	 */
107
+	status(usrId,status,callback){
108
+		var config = this.config;
109
+		var path = "/api/sipMgr/";
110
+		var paras= "action=sip_status&id=" + usrId +"&status=" + status;
111
+		var host = config.host;
112
+		var port = config.port;
113
+		this.postCall(host,port,path,paras,callback);
114
+	}
115
+
116
+	/**
117
+	 * 
118
+	 * @param {*} usrId 
119
+	 * @param {*} status  1 = online
120
+	 * @param {*} callback 
121
+	 */
122
+	play_status(usrId,status,callback){
123
+		var config = this.config;
124
+		var path = "/api/sipMgr/";
125
+		var paras= "action=play_status&id=" + usrId +"&status=" + status;
126
+		var host = config.host;
127
+		var port = config.port;
128
+		this.postCall(host,port,path,paras,callback);
129
+	}
130
+}
131
+
132
+module.exports = NtvAuthModule;

+ 12
- 32
package.json Переглянути файл

@@ -1,39 +1,19 @@
1 1
 {
2
-  "name": "gb28181-video-agent-gateway",
3
-  "version": "1.0.0",
4
-  "description": "A Node.js implementation of GB28181 Video Streaming Agent Server",
5
-  "main": "index.js",
2
+  "name": "gb28181-server",
6 3
   "scripts": {
7
-    "test": "node app.js"
4
+    "start": "nodemon ./vag.js",
5
+    "pm2": "pm2 start vag.jg --name gb28181",
6
+    "pkg": "pkg . -t node14-linux-x64 -o sip-server"
8 7
   },
9
-  "repository": {
10
-    "type": "git",
11
-    "url": "git+https://github.com/gb28181/vag.node.git"
8
+  "bin": "./vag.js",
9
+  "pkg": {
10
+
12 11
   },
13
-  "keywords": [
14
-    "gb28181",
15
-    "rtp",
16
-    "sip",
17
-    "PS"
18
-  ],
19
-  "author": "Yzxliu",
20
-  "license": "MIT",
21
-  "bugs": {
22
-    "url": "https://github.com/gb28181/vga.node/issue"
23
-  },
24
-  "homepage": "https://github.com/gb28181/vga.node",
25 12
   "dependencies": {
26
-    "basic-auth-connect": "^1.0.0",
27
-    "body-parser": "^1.19.0",
28
-    "chalk": "^4.1.0",
29
-    "dateformat": "^3.0.3",
30
-    "express": "^4.17.1",
31
-    "iconv-lite": "^0.6.0",
32
-    "rtp-rtcp": "0.0.2",
33
-    "ws": "^7.3.0",
34
-    "xml2js": "^0.4.23"
13
+
35 14
   },
36
-  "engines": {
37
-    "node": ">=8.0.0"
15
+  "devDependencies": {
16
+    "nodemon": "^14.15.4",
17
+    "pkg": "^4.4.9"
38 18
   }
39
-}
19
+}

+ 11
- 1
stream/server.js Переглянути файл

@@ -66,7 +66,7 @@ class NodeGB28181StreamServer {
66 66
         //RTP己处理好
67 67
         context.nodeEvent.on('rtpReadyed', this.rtpReceived.bind(this));
68 68
 
69
-        //停止播放,关闭推流客户端
69
+        //停止播放,关闭推流客户端(rtp客户端主动关闭连接)
70 70
         context.nodeEvent.on('stopPlayed', (ssrc) => {
71 71
             //ntv mod 转换成string,否者检索不到,ssrc传入的时number
72 72
             var key = '' + ssrc;
@@ -74,6 +74,16 @@ class NodeGB28181StreamServer {
74 74
                 let rtmpClient = context.publishers.get(key);
75 75
                 rtmpClient.stop();
76 76
                 context.publishers.delete(key);
77
+                Logger.log("rtmp session removed ",ssrc);
78
+            }
79
+        });
80
+
81
+        //ntv add 上一个函数主动关闭,或网络异常,或其他原因关闭rtmp
82
+        context.nodeEvent.on('rtmpClientClose', (ssrc) => {
83
+            var key = '' + ssrc;
84
+            if (context.publishers.has(key)) {
85
+                context.publishers.delete(key);
86
+                Logger.log("rtmp session removed ",ssrc);
77 87
             }
78 88
         });
79 89
     }

+ 0
- 1
stream/session.js Переглянути файл

@@ -49,7 +49,6 @@ class NodeGB28181StreamServerSession {
49 49
 
50 50
             //ntv add 
51 51
             if(this.ssrc){
52
-                console.log("emit stopPlayed ..........................=",this.ssrc);
53 52
                 context.nodeEvent.emit('stopPlayed',this.ssrc);
54 53
             }
55 54
 

Завантаження…
Відмінити
Зберегти