strophe.jingle.sdp.util.js 14 KB

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