You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SDPUtil.js 13KB

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