UNPKG

14.7 kBJavaScriptView Raw
1var grammar = module.exports = {
2 v: [{
3 name: 'version',
4 reg: /^(\d*)$/
5 }],
6 o: [{
7 // o=- 20518 0 IN IP4 203.0.113.1
8 // NB: sessionId will be a String in most cases because it is huge
9 name: 'origin',
10 reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,
11 names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],
12 format: '%s %s %d %s IP%d %s'
13 }],
14 // default parsing of these only (though some of these feel outdated)
15 s: [{ name: 'name' }],
16 i: [{ name: 'description' }],
17 u: [{ name: 'uri' }],
18 e: [{ name: 'email' }],
19 p: [{ name: 'phone' }],
20 z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly...
21 r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly
22 // k: [{}], // outdated thing ignored
23 t: [{
24 // t=0 0
25 name: 'timing',
26 reg: /^(\d*) (\d*)/,
27 names: ['start', 'stop'],
28 format: '%d %d'
29 }],
30 c: [{
31 // c=IN IP4 10.47.197.26
32 name: 'connection',
33 reg: /^IN IP(\d) (\S*)/,
34 names: ['version', 'ip'],
35 format: 'IN IP%d %s'
36 }],
37 b: [{
38 // b=AS:4000
39 push: 'bandwidth',
40 reg: /^(TIAS|AS|CT|RR|RS):(\d*)/,
41 names: ['type', 'limit'],
42 format: '%s:%s'
43 }],
44 m: [{
45 // m=video 51744 RTP/AVP 126 97 98 34 31
46 // NB: special - pushes to session
47 // TODO: rtp/fmtp should be filtered by the payloads found here?
48 reg: /^(\w*) (\d*) ([\w/]*)(?: (.*))?/,
49 names: ['type', 'port', 'protocol', 'payloads'],
50 format: '%s %d %s %s'
51 }],
52 a: [
53 {
54 // a=rtpmap:110 opus/48000/2
55 push: 'rtp',
56 reg: /^rtpmap:(\d*) ([\w\-.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,
57 names: ['payload', 'codec', 'rate', 'encoding'],
58 format: function (o) {
59 return (o.encoding)
60 ? 'rtpmap:%d %s/%s/%s'
61 : o.rate
62 ? 'rtpmap:%d %s/%s'
63 : 'rtpmap:%d %s';
64 }
65 },
66 {
67 // a=fmtp:108 profile-level-id=24;object=23;bitrate=64000
68 // a=fmtp:111 minptime=10; useinbandfec=1
69 push: 'fmtp',
70 reg: /^fmtp:(\d*) ([\S| ]*)/,
71 names: ['payload', 'config'],
72 format: 'fmtp:%d %s'
73 },
74 {
75 // a=control:streamid=0
76 name: 'control',
77 reg: /^control:(.*)/,
78 format: 'control:%s'
79 },
80 {
81 // a=rtcp:65179 IN IP4 193.84.77.194
82 name: 'rtcp',
83 reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,
84 names: ['port', 'netType', 'ipVer', 'address'],
85 format: function (o) {
86 return (o.address != null)
87 ? 'rtcp:%d %s IP%d %s'
88 : 'rtcp:%d';
89 }
90 },
91 {
92 // a=rtcp-fb:98 trr-int 100
93 push: 'rtcpFbTrrInt',
94 reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/,
95 names: ['payload', 'value'],
96 format: 'rtcp-fb:%s trr-int %d'
97 },
98 {
99 // a=rtcp-fb:98 nack rpsi
100 push: 'rtcpFb',
101 reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,
102 names: ['payload', 'type', 'subtype'],
103 format: function (o) {
104 return (o.subtype != null)
105 ? 'rtcp-fb:%s %s %s'
106 : 'rtcp-fb:%s %s';
107 }
108 },
109 {
110 // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
111 // a=extmap:1/recvonly URI-gps-string
112 // a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24
113 push: 'ext',
114 reg: /^extmap:(\d+)(?:\/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?/,
115 names: ['value', 'direction', 'encrypt-uri', 'uri', 'config'],
116 format: function (o) {
117 return (
118 'extmap:%d' +
119 (o.direction ? '/%s' : '%v') +
120 (o['encrypt-uri'] ? ' %s' : '%v') +
121 ' %s' +
122 (o.config ? ' %s' : '')
123 );
124 }
125 },
126 {
127 // a=extmap-allow-mixed
128 name: 'extmapAllowMixed',
129 reg: /^(extmap-allow-mixed)/
130 },
131 {
132 // a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32
133 push: 'crypto',
134 reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,
135 names: ['id', 'suite', 'config', 'sessionConfig'],
136 format: function (o) {
137 return (o.sessionConfig != null)
138 ? 'crypto:%d %s %s %s'
139 : 'crypto:%d %s %s';
140 }
141 },
142 {
143 // a=setup:actpass
144 name: 'setup',
145 reg: /^setup:(\w*)/,
146 format: 'setup:%s'
147 },
148 {
149 // a=connection:new
150 name: 'connectionType',
151 reg: /^connection:(new|existing)/,
152 format: 'connection:%s'
153 },
154 {
155 // a=mid:1
156 name: 'mid',
157 reg: /^mid:([^\s]*)/,
158 format: 'mid:%s'
159 },
160 {
161 // a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a
162 name: 'msid',
163 reg: /^msid:(.*)/,
164 format: 'msid:%s'
165 },
166 {
167 // a=ptime:20
168 name: 'ptime',
169 reg: /^ptime:(\d*(?:\.\d*)*)/,
170 format: 'ptime:%d'
171 },
172 {
173 // a=maxptime:60
174 name: 'maxptime',
175 reg: /^maxptime:(\d*(?:\.\d*)*)/,
176 format: 'maxptime:%d'
177 },
178 {
179 // a=sendrecv
180 name: 'direction',
181 reg: /^(sendrecv|recvonly|sendonly|inactive)/
182 },
183 {
184 // a=ice-lite
185 name: 'icelite',
186 reg: /^(ice-lite)/
187 },
188 {
189 // a=ice-ufrag:F7gI
190 name: 'iceUfrag',
191 reg: /^ice-ufrag:(\S*)/,
192 format: 'ice-ufrag:%s'
193 },
194 {
195 // a=ice-pwd:x9cml/YzichV2+XlhiMu8g
196 name: 'icePwd',
197 reg: /^ice-pwd:(\S*)/,
198 format: 'ice-pwd:%s'
199 },
200 {
201 // a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
202 name: 'fingerprint',
203 reg: /^fingerprint:(\S*) (\S*)/,
204 names: ['type', 'hash'],
205 format: 'fingerprint:%s %s'
206 },
207 {
208 // a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
209 // a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10
210 // a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10
211 // a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10
212 // a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10
213 push:'candidates',
214 reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/,
215 names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],
216 format: function (o) {
217 var str = 'candidate:%s %d %s %d %s %d typ %s';
218
219 str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';
220
221 // NB: candidate has three optional chunks, so %void middles one if it's missing
222 str += (o.tcptype != null) ? ' tcptype %s' : '%v';
223
224 if (o.generation != null) {
225 str += ' generation %d';
226 }
227
228 str += (o['network-id'] != null) ? ' network-id %d' : '%v';
229 str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';
230 return str;
231 }
232 },
233 {
234 // a=end-of-candidates (keep after the candidates line for readability)
235 name: 'endOfCandidates',
236 reg: /^(end-of-candidates)/
237 },
238 {
239 // a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...
240 name: 'remoteCandidates',
241 reg: /^remote-candidates:(.*)/,
242 format: 'remote-candidates:%s'
243 },
244 {
245 // a=ice-options:google-ice
246 name: 'iceOptions',
247 reg: /^ice-options:(\S*)/,
248 format: 'ice-options:%s'
249 },
250 {
251 // a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1
252 push: 'ssrcs',
253 reg: /^ssrc:(\d*) ([\w_-]*)(?::(.*))?/,
254 names: ['id', 'attribute', 'value'],
255 format: function (o) {
256 var str = 'ssrc:%d';
257 if (o.attribute != null) {
258 str += ' %s';
259 if (o.value != null) {
260 str += ':%s';
261 }
262 }
263 return str;
264 }
265 },
266 {
267 // a=ssrc-group:FEC 1 2
268 // a=ssrc-group:FEC-FR 3004364195 1080772241
269 push: 'ssrcGroups',
270 // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
271 reg: /^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/,
272 names: ['semantics', 'ssrcs'],
273 format: 'ssrc-group:%s %s'
274 },
275 {
276 // a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
277 name: 'msidSemantic',
278 reg: /^msid-semantic:\s?(\w*) (\S*)/,
279 names: ['semantic', 'token'],
280 format: 'msid-semantic: %s %s' // space after ':' is not accidental
281 },
282 {
283 // a=group:BUNDLE audio video
284 push: 'groups',
285 reg: /^group:(\w*) (.*)/,
286 names: ['type', 'mids'],
287 format: 'group:%s %s'
288 },
289 {
290 // a=rtcp-mux
291 name: 'rtcpMux',
292 reg: /^(rtcp-mux)/
293 },
294 {
295 // a=rtcp-rsize
296 name: 'rtcpRsize',
297 reg: /^(rtcp-rsize)/
298 },
299 {
300 // a=sctpmap:5000 webrtc-datachannel 1024
301 name: 'sctpmap',
302 reg: /^sctpmap:([\w_/]*) (\S*)(?: (\S*))?/,
303 names: ['sctpmapNumber', 'app', 'maxMessageSize'],
304 format: function (o) {
305 return (o.maxMessageSize != null)
306 ? 'sctpmap:%s %s %s'
307 : 'sctpmap:%s %s';
308 }
309 },
310 {
311 // a=x-google-flag:conference
312 name: 'xGoogleFlag',
313 reg: /^x-google-flag:([^\s]*)/,
314 format: 'x-google-flag:%s'
315 },
316 {
317 // a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0
318 push: 'rids',
319 reg: /^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/,
320 names: ['id', 'direction', 'params'],
321 format: function (o) {
322 return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';
323 }
324 },
325 {
326 // a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]
327 // a=imageattr:* send [x=800,y=640] recv *
328 // a=imageattr:100 recv [x=320,y=240]
329 push: 'imageattrs',
330 reg: new RegExp(
331 // a=imageattr:97
332 '^imageattr:(\\d+|\\*)' +
333 // send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]
334 '[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)' +
335 // recv [x=330,y=250]
336 '(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?'
337 ),
338 names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],
339 format: function (o) {
340 return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');
341 }
342 },
343 {
344 // a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8
345 // a=simulcast:recv 1;4,5 send 6;7
346 name: 'simulcast',
347 reg: new RegExp(
348 // a=simulcast:
349 '^simulcast:' +
350 // send 1,2,3;~4,~5
351 '(send|recv) ([a-zA-Z0-9\\-_~;,]+)' +
352 // space + recv 6;~7,~8
353 '(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?' +
354 // end
355 '$'
356 ),
357 names: ['dir1', 'list1', 'dir2', 'list2'],
358 format: function (o) {
359 return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');
360 }
361 },
362 {
363 // old simulcast draft 03 (implemented by Firefox)
364 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03
365 // a=simulcast: recv pt=97;98 send pt=97
366 // a=simulcast: send rid=5;6;7 paused=6,7
367 name: 'simulcast_03',
368 reg: /^simulcast:[\s\t]+([\S+\s\t]+)$/,
369 names: ['value'],
370 format: 'simulcast: %s'
371 },
372 {
373 // a=framerate:25
374 // a=framerate:29.97
375 name: 'framerate',
376 reg: /^framerate:(\d+(?:$|\.\d+))/,
377 format: 'framerate:%s'
378 },
379 {
380 // RFC4570
381 // a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5
382 name: 'sourceFilter',
383 reg: /^source-filter: *(excl|incl) (\S*) (IP4|IP6|\*) (\S*) (.*)/,
384 names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'],
385 format: 'source-filter: %s %s %s %s %s'
386 },
387 {
388 // a=bundle-only
389 name: 'bundleOnly',
390 reg: /^(bundle-only)/
391 },
392 {
393 // a=label:1
394 name: 'label',
395 reg: /^label:(.+)/,
396 format: 'label:%s'
397 },
398 {
399 // RFC version 26 for SCTP over DTLS
400 // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5
401 name: 'sctpPort',
402 reg: /^sctp-port:(\d+)$/,
403 format: 'sctp-port:%s'
404 },
405 {
406 // RFC version 26 for SCTP over DTLS
407 // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6
408 name: 'maxMessageSize',
409 reg: /^max-message-size:(\d+)$/,
410 format: 'max-message-size:%s'
411 },
412 {
413 // RFC7273
414 // a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37
415 push:'tsRefClocks',
416 reg: /^ts-refclk:([^\s=]*)(?:=(\S*))?/,
417 names: ['clksrc', 'clksrcExt'],
418 format: function (o) {
419 return 'ts-refclk:%s' + (o.clksrcExt != null ? '=%s' : '');
420 }
421 },
422 {
423 // RFC7273
424 // a=mediaclk:direct=963214424
425 name:'mediaClk',
426 reg: /^mediaclk:(?:id=(\S*))? *([^\s=]*)(?:=(\S*))?(?: *rate=(\d+)\/(\d+))?/,
427 names: ['id', 'mediaClockName', 'mediaClockValue', 'rateNumerator', 'rateDenominator'],
428 format: function (o) {
429 var str = 'mediaclk:';
430 str += (o.id != null ? 'id=%s %s' : '%v%s');
431 str += (o.mediaClockValue != null ? '=%s' : '');
432 str += (o.rateNumerator != null ? ' rate=%s' : '');
433 str += (o.rateDenominator != null ? '/%s' : '');
434 return str;
435 }
436 },
437 {
438 // a=keywds:keywords
439 name: 'keywords',
440 reg: /^keywds:(.+)$/,
441 format: 'keywds:%s'
442 },
443 {
444 // a=content:main
445 name: 'content',
446 reg: /^content:(.+)/,
447 format: 'content:%s'
448 },
449 // BFCP https://tools.ietf.org/html/rfc4583
450 {
451 // a=floorctrl:c-s
452 name: 'bfcpFloorCtrl',
453 reg: /^floorctrl:(c-only|s-only|c-s)/,
454 format: 'floorctrl:%s'
455 },
456 {
457 // a=confid:1
458 name: 'bfcpConfId',
459 reg: /^confid:(\d+)/,
460 format: 'confid:%s'
461 },
462 {
463 // a=userid:1
464 name: 'bfcpUserId',
465 reg: /^userid:(\d+)/,
466 format: 'userid:%s'
467 },
468 {
469 // a=floorid:1
470 name: 'bfcpFloorId',
471 reg: /^floorid:(.+) (?:m-stream|mstrm):(.+)/,
472 names: ['id', 'mStream'],
473 format: 'floorid:%s mstrm:%s'
474 },
475 {
476 // any a= that we don't understand is kept verbatim on media.invalid
477 push: 'invalid',
478 names: ['value']
479 }
480 ]
481};
482
483// set sensible defaults to avoid polluting the grammar with boring details
484Object.keys(grammar).forEach(function (key) {
485 var objs = grammar[key];
486 objs.forEach(function (obj) {
487 if (!obj.reg) {
488 obj.reg = /(.*)/;
489 }
490 if (!obj.format) {
491 obj.format = '%s';
492 }
493 });
494});