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.

strophe.jingle.sdp.util.js 13KB

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