您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

SDPUtil.js 13KB

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