Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

SDPUtil.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. var logger = require("jitsi-meet-logger").getLogger(__filename);
  2. var RTCBrowserType = require("../RTC/RTCBrowserType");
  3. SDPUtil = {
  4. filter_special_chars: function (text) {
  5. return text.replace(/[\\\/\{,\}\+]/g, "");
  6. },
  7. iceparams: function (mediadesc, sessiondesc) {
  8. var data = null;
  9. if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
  10. SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
  11. data = {
  12. ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
  13. pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
  14. };
  15. }
  16. return data;
  17. },
  18. parse_iceufrag: function (line) {
  19. return line.substring(12);
  20. },
  21. build_iceufrag: function (frag) {
  22. return 'a=ice-ufrag:' + frag;
  23. },
  24. parse_icepwd: function (line) {
  25. return line.substring(10);
  26. },
  27. build_icepwd: function (pwd) {
  28. return 'a=ice-pwd:' + pwd;
  29. },
  30. parse_mid: function (line) {
  31. return line.substring(6);
  32. },
  33. parse_mline: function (line) {
  34. var parts = line.substring(2).split(' '),
  35. data = {};
  36. data.media = parts.shift();
  37. data.port = parts.shift();
  38. data.proto = parts.shift();
  39. if (parts[parts.length - 1] === '') { // trailing whitespace
  40. parts.pop();
  41. }
  42. data.fmt = parts;
  43. return data;
  44. },
  45. build_mline: function (mline) {
  46. return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
  47. },
  48. parse_rtpmap: function (line) {
  49. var parts = line.substring(9).split(' '),
  50. data = {};
  51. data.id = parts.shift();
  52. parts = parts[0].split('/');
  53. data.name = parts.shift();
  54. data.clockrate = parts.shift();
  55. data.channels = parts.length ? parts.shift() : '1';
  56. return data;
  57. },
  58. /**
  59. * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
  60. * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
  61. * @returns [SCTP port number, protocol, streams]
  62. */
  63. parse_sctpmap: function (line)
  64. {
  65. var parts = line.substring(10).split(' ');
  66. var sctpPort = parts[0];
  67. var protocol = parts[1];
  68. // Stream count is optional
  69. var streamCount = parts.length > 2 ? parts[2] : null;
  70. return [sctpPort, protocol, streamCount];// SCTP port
  71. },
  72. build_rtpmap: function (el) {
  73. var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
  74. if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
  75. line += '/' + el.getAttribute('channels');
  76. }
  77. return line;
  78. },
  79. parse_crypto: function (line) {
  80. var parts = line.substring(9).split(' '),
  81. data = {};
  82. data.tag = parts.shift();
  83. data['crypto-suite'] = parts.shift();
  84. data['key-params'] = parts.shift();
  85. if (parts.length) {
  86. data['session-params'] = parts.join(' ');
  87. }
  88. return data;
  89. },
  90. parse_fingerprint: function (line) { // RFC 4572
  91. var parts = line.substring(14).split(' '),
  92. data = {};
  93. data.hash = parts.shift();
  94. data.fingerprint = parts.shift();
  95. // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
  96. return data;
  97. },
  98. parse_fmtp: function (line) {
  99. var parts = line.split(' '),
  100. i, key, value,
  101. data = [];
  102. parts.shift();
  103. parts = parts.join(' ').split(';');
  104. for (i = 0; i < parts.length; i++) {
  105. key = parts[i].split('=')[0];
  106. while (key.length && key[0] == ' ') {
  107. key = key.substring(1);
  108. }
  109. value = parts[i].split('=')[1];
  110. if (key && value) {
  111. data.push({name: key, value: value});
  112. } else if (key) {
  113. // rfc 4733 (DTMF) style stuff
  114. data.push({name: '', value: key});
  115. }
  116. }
  117. return data;
  118. },
  119. parse_icecandidate: function (line) {
  120. var candidate = {},
  121. elems = line.split(' ');
  122. candidate.foundation = elems[0].substring(12);
  123. candidate.component = elems[1];
  124. candidate.protocol = elems[2].toLowerCase();
  125. candidate.priority = elems[3];
  126. candidate.ip = elems[4];
  127. candidate.port = elems[5];
  128. // elems[6] => "typ"
  129. candidate.type = elems[7];
  130. candidate.generation = 0; // default value, may be overwritten below
  131. for (var i = 8; i < elems.length; i += 2) {
  132. switch (elems[i]) {
  133. case 'raddr':
  134. candidate['rel-addr'] = elems[i + 1];
  135. break;
  136. case 'rport':
  137. candidate['rel-port'] = elems[i + 1];
  138. break;
  139. case 'generation':
  140. candidate.generation = elems[i + 1];
  141. break;
  142. case 'tcptype':
  143. candidate.tcptype = elems[i + 1];
  144. break;
  145. default: // TODO
  146. logger.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
  147. }
  148. }
  149. candidate.network = '1';
  150. candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
  151. return candidate;
  152. },
  153. build_icecandidate: function (cand) {
  154. var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
  155. line += ' ';
  156. switch (cand.type) {
  157. case 'srflx':
  158. case 'prflx':
  159. case 'relay':
  160. if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
  161. line += 'raddr';
  162. line += ' ';
  163. line += cand['rel-addr'];
  164. line += ' ';
  165. line += 'rport';
  166. line += ' ';
  167. line += cand['rel-port'];
  168. line += ' ';
  169. }
  170. break;
  171. }
  172. if (cand.hasOwnAttribute('tcptype')) {
  173. line += 'tcptype';
  174. line += ' ';
  175. line += cand.tcptype;
  176. line += ' ';
  177. }
  178. line += 'generation';
  179. line += ' ';
  180. line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
  181. return line;
  182. },
  183. parse_ssrc: function (desc) {
  184. // proprietary mapping of a=ssrc lines
  185. // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
  186. // and parse according to that
  187. var lines = desc.split('\r\n'),
  188. data = {};
  189. for (var i = 0; i < lines.length; i++) {
  190. if (lines[i].substring(0, 7) == 'a=ssrc:') {
  191. var idx = lines[i].indexOf(' ');
  192. data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
  193. }
  194. }
  195. return data;
  196. },
  197. parse_rtcpfb: function (line) {
  198. var parts = line.substr(10).split(' ');
  199. var data = {};
  200. data.pt = parts.shift();
  201. data.type = parts.shift();
  202. data.params = parts;
  203. return data;
  204. },
  205. parse_extmap: function (line) {
  206. var parts = line.substr(9).split(' ');
  207. var data = {};
  208. data.value = parts.shift();
  209. if (data.value.indexOf('/') != -1) {
  210. data.direction = data.value.substr(data.value.indexOf('/') + 1);
  211. data.value = data.value.substr(0, data.value.indexOf('/'));
  212. } else {
  213. data.direction = 'both';
  214. }
  215. data.uri = parts.shift();
  216. data.params = parts;
  217. return data;
  218. },
  219. find_line: function (haystack, needle, sessionpart) {
  220. var lines = haystack.split('\r\n');
  221. for (var i = 0; i < lines.length; i++) {
  222. if (lines[i].substring(0, needle.length) == needle) {
  223. return lines[i];
  224. }
  225. }
  226. if (!sessionpart) {
  227. return false;
  228. }
  229. // search session part
  230. lines = sessionpart.split('\r\n');
  231. for (var j = 0; j < lines.length; j++) {
  232. if (lines[j].substring(0, needle.length) == needle) {
  233. return lines[j];
  234. }
  235. }
  236. return false;
  237. },
  238. find_lines: function (haystack, needle, sessionpart) {
  239. var lines = haystack.split('\r\n'),
  240. needles = [];
  241. for (var i = 0; i < lines.length; i++) {
  242. if (lines[i].substring(0, needle.length) == needle)
  243. needles.push(lines[i]);
  244. }
  245. if (needles.length || !sessionpart) {
  246. return needles;
  247. }
  248. // search session part
  249. lines = sessionpart.split('\r\n');
  250. for (var j = 0; j < lines.length; j++) {
  251. if (lines[j].substring(0, needle.length) == needle) {
  252. needles.push(lines[j]);
  253. }
  254. }
  255. return needles;
  256. },
  257. candidateToJingle: function (line) {
  258. // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
  259. // <candidate component=... foundation=... generation=... id=... ip=... network=... port=... priority=... protocol=... type=.../>
  260. if (line.indexOf('candidate:') === 0) {
  261. line = 'a=' + line;
  262. } else if (line.substring(0, 12) != 'a=candidate:') {
  263. logger.log('parseCandidate called with a line that is not a candidate line');
  264. logger.log(line);
  265. return null;
  266. }
  267. if (line.substring(line.length - 2) == '\r\n') // chomp it
  268. line = line.substring(0, line.length - 2);
  269. var candidate = {},
  270. elems = line.split(' '),
  271. i;
  272. if (elems[6] != 'typ') {
  273. logger.log('did not find typ in the right place');
  274. logger.log(line);
  275. return null;
  276. }
  277. candidate.foundation = elems[0].substring(12);
  278. candidate.component = elems[1];
  279. candidate.protocol = elems[2].toLowerCase();
  280. candidate.priority = elems[3];
  281. candidate.ip = elems[4];
  282. candidate.port = elems[5];
  283. // elems[6] => "typ"
  284. candidate.type = elems[7];
  285. candidate.generation = '0'; // default, may be overwritten below
  286. for (i = 8; i < elems.length; i += 2) {
  287. switch (elems[i]) {
  288. case 'raddr':
  289. candidate['rel-addr'] = elems[i + 1];
  290. break;
  291. case 'rport':
  292. candidate['rel-port'] = elems[i + 1];
  293. break;
  294. case 'generation':
  295. candidate.generation = elems[i + 1];
  296. break;
  297. case 'tcptype':
  298. candidate.tcptype = elems[i + 1];
  299. break;
  300. default: // TODO
  301. logger.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
  302. }
  303. }
  304. candidate.network = '1';
  305. candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
  306. return candidate;
  307. },
  308. candidateFromJingle: function (cand) {
  309. var line = 'a=candidate:';
  310. line += cand.getAttribute('foundation');
  311. line += ' ';
  312. line += cand.getAttribute('component');
  313. line += ' ';
  314. var protocol = cand.getAttribute('protocol');
  315. // use tcp candidates for FF
  316. if (RTCBrowserType.isFirefox() && protocol.toLowerCase() == 'ssltcp') {
  317. protocol = 'tcp';
  318. }
  319. line += protocol; //.toUpperCase(); // chrome M23 doesn't like this
  320. line += ' ';
  321. line += cand.getAttribute('priority');
  322. line += ' ';
  323. line += cand.getAttribute('ip');
  324. line += ' ';
  325. line += cand.getAttribute('port');
  326. line += ' ';
  327. line += 'typ';
  328. line += ' ' + cand.getAttribute('type');
  329. line += ' ';
  330. switch (cand.getAttribute('type')) {
  331. case 'srflx':
  332. case 'prflx':
  333. case 'relay':
  334. if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
  335. line += 'raddr';
  336. line += ' ';
  337. line += cand.getAttribute('rel-addr');
  338. line += ' ';
  339. line += 'rport';
  340. line += ' ';
  341. line += cand.getAttribute('rel-port');
  342. line += ' ';
  343. }
  344. break;
  345. }
  346. if (protocol.toLowerCase() == 'tcp') {
  347. line += 'tcptype';
  348. line += ' ';
  349. line += cand.getAttribute('tcptype');
  350. line += ' ';
  351. }
  352. line += 'generation';
  353. line += ' ';
  354. line += cand.getAttribute('generation') || '0';
  355. return line + '\r\n';
  356. }
  357. };
  358. module.exports = SDPUtil;