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

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