camstreamerlib 1.4.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/RtspClient.js DELETED
@@ -1,327 +0,0 @@
1
- const net = require('net');
2
- const util = require('util');
3
- const EventEmitter = require('events');
4
-
5
- const Digest = require('./Digest');
6
-
7
- function RtspClient(options) {
8
- this.ip = '127.0.0.1';
9
- this.port = 80;
10
- this.auth = '';
11
- if (options) {
12
- this.ip = options['ip'] || this.ip;
13
- this.port = options['port'] || 80;
14
- this.auth = options['auth'] || this.auth;
15
- }
16
-
17
- this.authorizationType = 'basic';
18
- this.wwwAuthenticateHeader = '';
19
- this.sessioncookie = Math.random().toString(36).substring(7);
20
- this.clientGet = null;
21
- this.clientPost = null;
22
- this.imputBuffer = null;
23
- this.state = 'HTTP_INIT';
24
- this.rtspPath = '';
25
- this.rtspCSeq = 0;
26
- this.control = '';
27
- this.session = '';
28
- this.authorizationSent = false;
29
-
30
- this.keepAliveTimer = null;
31
- this.disconnected = false;
32
- this.rtpMsgBuffer = '';
33
-
34
- EventEmitter.call(this);
35
- }
36
-
37
- util.inherits(RtspClient, EventEmitter);
38
-
39
- RtspClient.prototype.connect = function(eventTopicFilter) {
40
- this.rtspPath = '/axis-media/media.amp?video=0&audio=0&event=on';
41
- if (eventTopicFilter && eventTopicFilter.length) {
42
- this.rtspPath += '&eventtopic=' + eventTopicFilter;
43
- }
44
-
45
- // Start GET connection
46
- this.clientGet = new net.Socket();
47
- this.clientGet.connect(this.port, this.ip, function() {
48
- //console.log('Connected GET');
49
- this.clientGet.write(this.getInitializationMessageGet());
50
- }.bind(this));
51
-
52
- this.clientGet.on('data', this.processGetMessage.bind(this));
53
-
54
- this.clientGet.on('close', this.closeConnection.bind(this));
55
-
56
- // Start POST connection
57
- this.clientPost = new net.Socket();
58
- this.clientPost.connect(this.port, this.ip, function() {
59
- //console.log('Connected POST');
60
- this.clientPost.write(this.getInitializationMessagePost());
61
- }.bind(this));
62
-
63
- this.clientPost.on('data', function(data) {
64
- //console.log('Received POST: ' + data);
65
- }.bind(this));
66
-
67
- this.clientPost.on('close', this.closeConnection.bind(this));
68
- }
69
-
70
- RtspClient.prototype.disconnect = function() {
71
- this.closeConnection('');
72
- }
73
-
74
- RtspClient.prototype.closeConnection = function(reason) {
75
- if (!this.disconnected) {
76
- this.disconnected = true;
77
- this.emit('disconnect', reason);
78
- }
79
-
80
- clearInterval(this.keepAliveTimer);
81
- this.clientGet.destroy();
82
- this.clientPost.destroy();
83
- }
84
-
85
- RtspClient.prototype.processGetMessage = function(data) {
86
- if (!this.inputBuffer) {
87
- this.inputBuffer = Buffer.from(data,'binary');
88
- }
89
- else {
90
- this.inputBuffer = Buffer.concat([this.inputBuffer, Buffer.from(data,'binary')]);
91
- }
92
-
93
- if (this.state == 'HTTP_INIT') {
94
- var msg = this.parseRtspMessage();
95
- if (msg) {
96
- if (msg.statusLine.indexOf('200 OK') != -1) {
97
- this.state = 'RTSP_OPTION';
98
- this.sendRtspMessage(this.getRtspMessage());
99
- } else if (msg.statusLine.indexOf('401') != -1 && !this.authorizationSent) {
100
- this.authorizationSent = true;
101
- this.authorizationType = 'digest';
102
- this.wwwAuthenticateHeader = msg.headers['www-authenticate'];
103
- this.clientGet.write(this.getInitializationMessageGet());
104
- } else {
105
- this.closeConnection(msg.statusLine);
106
- }
107
- }
108
- return;
109
- }
110
-
111
- while (this.inputBuffer.length > 0) {
112
- var msgType = '';
113
- var firstChar = String.fromCharCode(this.inputBuffer[0]);
114
- if (firstChar == 'R') {
115
- msgType = 'RTSP';
116
- } else if (firstChar == '$') {
117
- msgType = 'RTP';
118
- } else {
119
- this.closeConnection('Unknown message found: ' + this.inputBuffer);
120
- return;
121
- }
122
-
123
- if (msgType == 'RTSP') {
124
- var msg = this.parseRtspMessage();
125
- if (!msg) {
126
- break;
127
- }
128
-
129
- if (msg.statusLine.indexOf('401') != -1 && !this.authorizationSent) {
130
- this.authorizationSent = true;
131
- this.authorizationType = 'digest';
132
- this.wwwAuthenticateHeader = msg.headers['www-authenticate'];
133
- this.sendRtspMessage(this.getRtspMessage());
134
- return;
135
- }
136
-
137
- if (this.state != 'HTTP_INIT' && msg.statusLine.indexOf('RTSP/1.0 200 OK') == -1) {
138
- this.closeConnection('Invalid RTSP response: ' + msg.headersRaw + msg.body);
139
- return;
140
- }
141
-
142
- this.authorizationSent = false; // Authorization expires for digest method
143
- switch (this.state) {
144
- case 'RTSP_OPTION': {
145
- this.state = 'RTSP_DESCRIBE';
146
- this.sendRtspMessage(this.getRtspMessage());
147
- break;
148
- }
149
- case 'RTSP_DESCRIBE': {
150
- const regex = /(a=control):(.*)/g;
151
- this.control = regex.exec(msg.body)[2];
152
- this.state = 'RTSP_SETUP';
153
- this.sendRtspMessage(this.getRtspMessage());
154
- break;
155
- }
156
- case 'RTSP_SETUP': {
157
- this.session = msg.headers['session'].split(';')[0];
158
- this.state = 'RTSP_PLAY';
159
- this.sendRtspMessage(this.getRtspMessage());
160
- break;
161
- }
162
- case 'RTSP_PLAY': {
163
- this.emit('connect');
164
- this.state = 'RTSP_GET_PARAMETER';
165
- this.keepAliveTimer = setInterval(function() {
166
- this.sendRtspMessage(this.getRtspMessage());
167
- }.bind(this), 30000);
168
- break;
169
- }
170
- }
171
- } else if (msgType == 'RTP') {
172
- var msg = this.parseRtpMessage();
173
- if (!msg) {
174
- break;
175
- }
176
-
177
- if (msg.channel == 0) {
178
- this.rtpMsgBuffer += msg.body.toString();
179
- while (true) {
180
- var msgEnd = this.rtpMsgBuffer.indexOf('</tt:MetadataStream>');
181
- if (msgEnd == -1) {
182
- break;
183
- }
184
- this.emit('event', this.rtpMsgBuffer.substring(0, msgEnd + 20));
185
- this.rtpMsgBuffer = this.rtpMsgBuffer.substring(msgEnd + 20);
186
- }
187
- }
188
- }
189
- }
190
- }
191
-
192
- RtspClient.prototype.parseRtspMessage = function() {
193
- var msgEndFound = this.inputBuffer.indexOf('\r\n\r\n');
194
- if (msgEndFound == -1) {
195
- return null;
196
- }
197
-
198
- var msgHeaders = this.inputBuffer.slice(0, msgEndFound + 4);
199
- const headers = {};
200
- const regex = /([\w-]+): (.*)/g;
201
- var tmp;
202
- while (tmp = regex.exec(msgHeaders)) {
203
- headers[tmp[1].toLowerCase()] = tmp[2];
204
- }
205
-
206
- var msgSize = msgEndFound + 4;
207
- if (headers['content-length'] != undefined) {
208
- msgSize += parseInt(headers['content-length']);
209
- }
210
- var body = this.inputBuffer.slice(msgEndFound + 4, msgSize);
211
- this.inputBuffer = this.inputBuffer.slice(msgSize);
212
-
213
- return {'statusLine': msgHeaders.slice(0, msgHeaders.indexOf('\r\n')), 'headers': headers, 'headersRaw': msgHeaders, 'body': body};
214
- }
215
-
216
- RtspClient.prototype.parseRtpMessage = function() {
217
- const INTERLEAVED_HEADER_SIZE = 4;
218
- const RTP_HEADER_SIZE = 12;
219
- if (this.inputBuffer.length < INTERLEAVED_HEADER_SIZE) { // Not enough data even for rtp header. Try again when more data is available
220
- return null;
221
- }
222
-
223
- var channel = this.inputBuffer[1]; // Interleaved channel, 0 - RTP, 1 - RTCP
224
- var msgDataSize = (this.inputBuffer[2] << 8) | this.inputBuffer[3];
225
- msgDataSize += INTERLEAVED_HEADER_SIZE;
226
-
227
- // Ivalid input data - invalid channel
228
- if (channel < 0 || channel > 1)
229
- {
230
- this.closeConnection("InterleavedMessage - invalid channel found: " + channel);
231
- return null;
232
- }
233
-
234
- // The complete RTP package is not here yet, wait for more data
235
- if (msgDataSize > this.inputBuffer.length) {
236
- return null;
237
- }
238
-
239
- var body = this.inputBuffer.slice(INTERLEAVED_HEADER_SIZE + RTP_HEADER_SIZE, msgDataSize);
240
- this.inputBuffer = this.inputBuffer.slice(msgDataSize);
241
- return {'channel': channel, 'body': body};
242
- }
243
-
244
- RtspClient.prototype.getInitializationMessageGet = function() {
245
- var data = 'GET /axis-media/media.amp HTTP/1.1\r\n' +
246
- 'CSeq: 1\r\n' +
247
- 'User-Agent: Camstreamer RTSP Client\r\n' +
248
- 'Host: ' + this.ip + '\r\n' +
249
- 'x-sessioncookie: ' + this.sessioncookie + '\r\n' +
250
- 'Accept: application/x-rtsp-tunnelled\r\n' +
251
- 'Pragma: no-cache\r\n' +
252
- 'Authorization: ' + this.getAuthHeader('GET', '/axis-media/media.amp') + '\r\n' +
253
- 'Cache-Control: no-cache\r\n\r\n';
254
- return data;
255
- }
256
-
257
- RtspClient.prototype.getInitializationMessagePost = function() {
258
- var data = 'POST /axis-media/media.amp HTTP/1.1\r\n' +
259
- 'CSeq: 1\r\n' +
260
- 'User-Agent: Camstreamer RTSP Client\r\n' +
261
- 'Host: ' + this.ip + '\r\n' +
262
- 'x-sessioncookie: ' + this.sessioncookie + '\r\n' +
263
- 'Content-Type: application/x-rtsp-tunnelled\r\n' +
264
- 'Pragma: no-cache\r\n' +
265
- 'Cache-Control: no-cache\r\n' +
266
- 'Authorization: ' + this.getAuthHeader('POST', '/axis-media/media.amp') + '\r\n' +
267
- 'Content-Length: 32767\r\n\r\n';
268
- return data;
269
- }
270
-
271
- RtspClient.prototype.getRtspMessage = function() {
272
- this.rtspCSeq++;
273
- switch (this.state) {
274
- case 'RTSP_OPTION': {
275
- return 'OPTIONS ' + this.rtspPath + ' RTSP/1.0\r\n' +
276
- 'CSeq: ' + this.rtspCSeq + '\r\n' +
277
- 'User-Agent: Camstreamer RTSP Client\r\n' +
278
- 'Authorization: ' + this.getAuthHeader('OPTIONS', this.rtspPath) + '\r\n\r\n';
279
- }
280
- case 'RTSP_DESCRIBE': {
281
- return 'DESCRIBE ' + this.rtspPath + ' RTSP/1.0\r\n' +
282
- 'CSeq: ' + this.rtspCSeq + '\r\n' +
283
- 'User-Agent: Camstreamer RTSP Client\r\n' +
284
- 'Authorization: ' + this.getAuthHeader('DESCRIBE', this.rtspPath) + '\r\n' +
285
- 'Accept: application/sdp\r\n\r\n';
286
- }
287
- case 'RTSP_SETUP': {
288
- return 'SETUP ' + this.control + ' RTSP/1.0\r\n' +
289
- 'CSeq: ' + this.rtspCSeq + '\r\n' +
290
- 'User-Agent: Camstreamer RTSP Client\r\n' +
291
- 'Authorization: ' + this.getAuthHeader('SETUP', this.rtspPath) + '\r\n' +
292
- 'Transport: RTP/AVP/TCP;unicast;interleaved=0-1\r\n\r\n';
293
- }
294
- case 'RTSP_PLAY': {
295
- return 'PLAY ' + this.rtspPath + ' RTSP/1.0\r\n' +
296
- 'CSeq: ' + this.rtspCSeq + '\r\n' +
297
- 'User-Agent: Camstreamer RTSP Client\r\n' +
298
- 'Authorization: ' + this.getAuthHeader('PLAY', this.rtspPath) + '\r\n' +
299
- 'Session: ' + this.session + '\r\n' +
300
- 'Range: npt=0.000-\r\n\r\n'
301
- }
302
- case 'RTSP_GET_PARAMETER': {
303
- return 'GET_PARAMETER ' + this.rtspPath + ' RTSP/1.0\r\n' +
304
- 'CSeq: ' + this.rtspCSeq + '\r\n' +
305
- 'User-Agent: Camstreamer RTSP Client\r\n' +
306
- 'Authorization: ' + this.getAuthHeader('GET_PARAMETER', this.rtspPath) + '\r\n' +
307
- 'Session: ' + this.session + '\r\n\r\n';
308
- }
309
- }
310
- }
311
-
312
- RtspClient.prototype.getAuthHeader = function(method, path) {
313
- if (this.authorizationType == 'basic') {
314
- return 'Basic ' + Buffer.from(this.auth).toString('base64');
315
- } else {
316
- var userPass = this.auth.split(':');
317
- return Digest.getAuthHeader(userPass[0], userPass[1], method, path, this.wwwAuthenticateHeader);
318
- }
319
- }
320
-
321
- RtspClient.prototype.sendRtspMessage = function(message) {
322
- //console.log(message);
323
- var msgBase64 = Buffer.from(message).toString('base64');
324
- this.clientPost.write(msgBase64);
325
- }
326
-
327
- module.exports = RtspClient;