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.

825 lines
27KB

  1. #include "sdp-a-webrtc.h"
  2. #include "sdp.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <assert.h>
  7. #if defined(OS_WINDOWS)
  8. #define strncasecmp _strnicmp
  9. #endif
  10. struct sdp_string_token_t
  11. {
  12. const char* p;
  13. int n;
  14. };
  15. struct sdp_string_parser_t
  16. {
  17. const char* s; // start
  18. const char* e; // end
  19. };
  20. static inline int sdp_get_token(struct sdp_string_parser_t* s, struct sdp_string_token_t* t, const char* escape)
  21. {
  22. // left trim
  23. while (s->s && s->s < s->e && ' ' == *s->s)
  24. ++s->s;
  25. t->p = s->s;
  26. for (t->n = 0; s->s && s->s + t->n < s->e; ++t->n)
  27. {
  28. if (strchr(escape, s->s[t->n]))
  29. break;
  30. }
  31. s->s += t->n + 1; // skip escape
  32. return t->n;
  33. }
  34. static inline int sdp_string_count(const char* s, int n, char c)
  35. {
  36. int i;
  37. for(i = 0; s && n > 0; n--)
  38. {
  39. if (*s++ == c)
  40. i++;
  41. }
  42. return i;
  43. }
  44. static inline void sdp_strcpy(char* p, const char* s, int n)
  45. {
  46. memcpy(p, s, n);
  47. p[n] = '\0';
  48. }
  49. static inline char* sdp_strdup(const char* s, int n)
  50. {
  51. char* p;
  52. p = malloc(n + 1);
  53. if (p)
  54. {
  55. memcpy(p, s, n);
  56. p[n] = '\0';
  57. }
  58. return p;
  59. }
  60. static char** sdp_string_split(const char* s, int n, const char* c, int* num)
  61. {
  62. int i, cn;
  63. char* pv, **pk;
  64. struct sdp_string_parser_t t;
  65. struct sdp_string_token_t k;
  66. t.s = s;
  67. t.e = s + n;
  68. *num = 0;
  69. if (t.s >= t.e)
  70. return NULL;
  71. cn = sdp_string_count(t.s, n, * c) + 1;
  72. pk = (char**)malloc(t.e - t.s + 1 + sizeof(char*) * cn);
  73. if (pk)
  74. {
  75. pv = (char*)(pk + cn);
  76. sdp_strcpy(pv, t.s, n);
  77. t.e = pv + (t.e - t.s);
  78. t.s = pv;
  79. for (i = 0; i < cn && sdp_get_token(&t, &k, c); i++)
  80. {
  81. assert(k.p + k.n <= t.e);
  82. pk[i] = (char*)k.p;
  83. ((char*)k.p)[k.n] = '\0';
  84. }
  85. *num = i;
  86. }
  87. return pk;
  88. }
  89. /// @return 0-ok, other-error
  90. static inline int sdp_str2int(const char* s, int n, int* v)
  91. {
  92. char* e;
  93. *v = (int)strtoul(s, &e, 10);
  94. return e - s == n ? 0 : -1;
  95. }
  96. // "extmap:" 1*5DIGIT ["/" direction]
  97. // direction = "sendonly" / "recvonly" / "sendrecv" / "inactive"
  98. int sdp_a_extmap(const char* s, int n, int* ext, int* direction, char url[128])
  99. {
  100. struct sdp_string_parser_t t;
  101. struct sdp_string_token_t k, v, d;
  102. t.s = s;
  103. t.e = s + n;
  104. if (!sdp_get_token(&t, &k, "/ ") || 0 != sdp_str2int(k.p, k.n, ext))
  105. return -1;
  106. // direction
  107. if (direction)
  108. *direction = SDP_A_SENDRECV;
  109. if (t.s > s && '/' == *(t.s-1))
  110. {
  111. if (!sdp_get_token(&t, &d, " ") || d.n != 8)
  112. return -1;
  113. if (direction)
  114. {
  115. if (0 == strncmp(d.p, "sendonly", d.n))
  116. *direction = SDP_A_SENDONLY;
  117. else if (0 == strncmp(d.p, "recvonly", d.n))
  118. *direction = SDP_A_RECVONLY;
  119. else if (0 == strncmp(d.p, "sendrecv", d.n))
  120. *direction = SDP_A_SENDRECV;
  121. else if (0 == strncmp(d.p, "inactive", d.n))
  122. *direction = SDP_A_INACTIVE;
  123. }
  124. }
  125. if(!sdp_get_token(&t, &v, " ") || v.n >= 128)
  126. return -1;
  127. sdp_strcpy(url, v.p, v.n);
  128. return 0;
  129. }
  130. int sdp_a_fmtp(const char* s, int n, int* fmt, char** param)
  131. {
  132. struct sdp_string_parser_t t;
  133. struct sdp_string_token_t k, v;
  134. t.s = s;
  135. t.e = s + n;
  136. if (!sdp_get_token(&t, &k, " ") || 0 != sdp_str2int(k.p, k.n, fmt) || !sdp_get_token(&t, &v, " "))
  137. return -1;
  138. *param = sdp_strdup(v.p, v.n);
  139. return 0;
  140. }
  141. /*
  142. * https://www.rfc-editor.org/rfc/rfc8122.html#section-5
  143. * attribute =/ fingerprint-attribute
  144. fingerprint-attribute = "fingerprint" ":" hash-func SP fingerprint
  145. hash-func = "sha-1" / "sha-224" / "sha-256" /
  146. "sha-384" / "sha-512" /
  147. "md5" / "md2" / token
  148. ; Additional hash functions can only come
  149. ; from updates to RFC 3279
  150. fingerprint = 2UHEX *(":" 2UHEX)
  151. ; Each byte in upper-case hex, separated
  152. ; by colons.
  153. UHEX = DIGIT / %x41-46 ; A-F uppercase
  154. */
  155. int sdp_a_fingerprint(const char* s, int n, char hash[16], char fingerprint[128])
  156. {
  157. struct sdp_string_parser_t t;
  158. struct sdp_string_token_t k, v;
  159. t.s = s;
  160. t.e = s + n;
  161. if (!sdp_get_token(&t, &k, " ") || !sdp_get_token(&t, &v, " "))
  162. return -1;
  163. sdp_strcpy(hash, k.p, k.n);
  164. sdp_strcpy(fingerprint, v.p, v.n);
  165. return 0;
  166. }
  167. /*
  168. * https://www.rfc-editor.org/rfc/rfc5888.html#section-5
  169. * group-attribute = "a=group:" semantics *(SP identification-tag)
  170. * semantics = "LS" / "FID" / semantics-extension
  171. * semantics-extension = token ; token is defined in RFC 4566
  172. *
  173. * a=group:LS 1 2
  174. * a=group:BUNDLE audio video
  175. */
  176. int sdp_a_group(const char* s, int n, char semantics[32], char*** groups, int* count)
  177. {
  178. struct sdp_string_parser_t t;
  179. struct sdp_string_token_t k;
  180. t.s = s;
  181. t.e = s + n;
  182. if (!sdp_get_token(&t, &k, " "))
  183. return -1;
  184. sdp_strcpy(semantics, k.p, k.n);
  185. *count = 0;
  186. *groups = sdp_string_split(t.s, (int)(intptr_t)(t.e - t.s), " ", count);
  187. return 0;
  188. }
  189. /*
  190. * https://www.rfc-editor.org/rfc/rfc8839#name-candidate-attribute
  191. * candidate-attribute = "candidate" ":" foundation SP component-id SP
  192. transport SP
  193. priority SP
  194. connection-address SP ;from RFC 4566
  195. port ;port from RFC 4566
  196. SP cand-type
  197. [SP rel-addr]
  198. [SP rel-port]
  199. *(SP cand-extension)
  200. foundation = 1*32ice-char
  201. component-id = 1*3DIGIT
  202. transport = "UDP" / transport-extension
  203. transport-extension = token ; from RFC 3261
  204. priority = 1*10DIGIT
  205. cand-type = "typ" SP candidate-types
  206. candidate-types = "host" / "srflx" / "prflx" / "relay" / token
  207. rel-addr = "raddr" SP connection-address
  208. rel-port = "rport" SP port
  209. cand-extension = extension-att-name SP extension-att-value
  210. extension-att-name = token
  211. extension-att-value = *VCHAR
  212. ice-char = ALPHA / DIGIT / "+" / "/"
  213. * a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr 203.0.113.141 rport 8998
  214. */
  215. int sdp_a_ice_candidate(const char* s, int n, struct sdp_ice_candidate_t* c)
  216. {
  217. int i, val;
  218. int uid, uport, upriority;
  219. struct sdp_string_parser_t t;
  220. struct sdp_string_token_t foundation, component, transport, priority, address, port, candtype;
  221. t.s = s;
  222. t.e = s + n;
  223. if (!sdp_get_token(&t, &foundation, " ") || foundation.n >= sizeof(c->foundation)
  224. || !sdp_get_token(&t, &component, " ") || 0 != sdp_str2int(component.p, component.n, &uid)
  225. || !sdp_get_token(&t, &transport, " ") || transport.n >= sizeof(c->transport)
  226. || !sdp_get_token(&t, &priority, " ") || 0 != sdp_str2int(priority.p, priority.n, &upriority)
  227. || !sdp_get_token(&t, &address, " ") || address.n >= sizeof(c->address)
  228. || !sdp_get_token(&t, &port, " ") || 0 != sdp_str2int(port.p, port.n, &uport)
  229. || !sdp_get_token(&t, &candtype, " ") || 3 != candtype.n || 0 != strncmp("typ", candtype.p, candtype.n)
  230. || !sdp_get_token(&t, &candtype, " ") || candtype.n >= sizeof(c->candtype))
  231. return -1;
  232. sdp_strcpy(c->foundation, foundation.p, foundation.n);
  233. sdp_strcpy(c->transport, transport.p, transport.n);
  234. sdp_strcpy(c->candtype, candtype.p, candtype.n);
  235. sdp_strcpy(c->address, address.p, address.n);
  236. c->component = (uint16_t)uid;
  237. c->priority = (uint32_t)upriority;
  238. c->port = (uint16_t)uport;
  239. c->relport = 0;
  240. c->generation = 0;
  241. c->nextension = 0;
  242. if (t.e > t.s)
  243. c->extensions = sdp_string_split(t.s, (int)(intptr_t)(t.e - t.s), " ", &c->nextension);
  244. for (i = 0; i + 1 < c->nextension; i += 2)
  245. {
  246. if (0 == strcmp("raddr", c->extensions[i]) && strlen(c->extensions[i + 1]) < sizeof(c->reladdr))
  247. {
  248. snprintf(c->reladdr, sizeof(c->reladdr), "%s", c->extensions[i + 1]);
  249. }
  250. if (0 == strcmp("rport", c->extensions[i]) && 0 == sdp_str2int(c->extensions[i + 1], (int)strlen(c->extensions[i + 1]), &val))
  251. {
  252. c->relport = (uint16_t)val;
  253. }
  254. else if (0 == strcmp("generation", c->extensions[i]) && 0 == sdp_str2int(c->extensions[i + 1], (int)strlen(c->extensions[i + 1]), &val))
  255. {
  256. c->generation = val;
  257. }
  258. }
  259. return 0;
  260. }
  261. /*
  262. * https://www.rfc-editor.org/rfc/rfc8839#name-remote-candidates-attribute
  263. * remote-candidate-att = "remote-candidates:" remote-candidate 0*(SP remote-candidate)
  264. * remote-candidate = component-id SP connection-address SP port
  265. *
  266. * a=remote-candidates:1 192.0.2.3 45664
  267. */
  268. int sdp_a_ice_remote_candidates(const char* s, int n, struct sdp_ice_candidate_t* c)
  269. {
  270. // TODO: multiple remote candidates
  271. int uid, uport;
  272. struct sdp_string_parser_t t;
  273. struct sdp_string_token_t component, address, port;
  274. t.s = s;
  275. t.e = s + n;
  276. if (!sdp_get_token(&t, &component, " ") || 0 != sdp_str2int(component.p, component.n, &uid)
  277. || !sdp_get_token(&t, &address, " ") || address.n >= sizeof(c->address)
  278. || !sdp_get_token(&t, &port, " ") || 0 != sdp_str2int(port.p, port.n, &uport))
  279. return -1;
  280. sdp_strcpy(c->address, address.p, address.n);
  281. c->component = (uint16_t)uid;
  282. c->port = (uint16_t)uport;
  283. return 0;
  284. }
  285. /*
  286. * https://www.rfc-editor.org/rfc/rfc8839#name-ice-pacing-attribute
  287. * ice-pacing-att = "ice-pacing:" pacing-value
  288. * pacing-value = 1*10DIGIT
  289. *
  290. * a=ice-pacing:50
  291. */
  292. int sdp_a_ice_pacing(const char* s, int n, int* pacing)
  293. {
  294. if (0 != sdp_str2int(s, n, pacing))
  295. return -1;
  296. return 0;
  297. }
  298. /*
  299. * https://www.rfc-editor.org/rfc/rfc8839#name-ice-options-attribute
  300. * ice-options = "ice-options:" ice-option-tag *(SP ice-option-tag)
  301. * ice-option-tag = 1*ice-char
  302. *
  303. * a=ice-options:ice2 rtp+ecn
  304. */
  305. int sdp_a_ice_options(const char* s, int n, char*** options, int* count)
  306. {
  307. *count = 0;
  308. *options = sdp_string_split(s, n, " ", count);
  309. return 0;
  310. }
  311. /*
  312. * https://www.rfc-editor.org/rfc/rfc5888.html#section-4
  313. * mid-attribute = "a=mid:" identification-tag
  314. * identification-tag = token ; token is defined in RFC 4566
  315. */
  316. int sdp_a_mid(const char* s, int n, char tag[256])
  317. {
  318. struct sdp_string_parser_t t;
  319. struct sdp_string_token_t k;
  320. t.s = s;
  321. t.e = s + n;
  322. if (!sdp_get_token(&t, &k, " ") || k.n > 256)
  323. return -1;
  324. sdp_strcpy(tag, k.p, k.n);
  325. return 0;
  326. }
  327. /*
  328. * https://www.rfc-editor.org/rfc/rfc8830#name-attribute-registration-in-e
  329. * msid-value = msid-id [ SP msid-appdata ]
  330. * msid-id = 1*64token-char ; see RFC 4566
  331. * msid-appdata = 1*64token-char ; see RFC 4566
  332. *
  333. * a=msid:examplefoo examplebar
  334. */
  335. int sdp_a_msid(const char* s, int n, char id[65], char appdata[65])
  336. {
  337. struct sdp_string_parser_t t;
  338. struct sdp_string_token_t k, v;
  339. t.s = s;
  340. t.e = s + n;
  341. if (!sdp_get_token(&t, &k, " ") || k.n > 64)
  342. return -1;
  343. sdp_strcpy(id, k.p, k.n);
  344. appdata[0] = '\0';
  345. if (sdp_get_token(&t, &v, " ") && v.n <= 64)
  346. sdp_strcpy(appdata, v.p, v.n);
  347. return 0;
  348. }
  349. /*
  350. * https://www.rfc-editor.org/rfc/rfc8866#name-orient-orientation
  351. * orient-value = portrait / landscape / seascape
  352. * a=orient:portrait
  353. */
  354. int sdp_a_orient(const char* s, int n, char orient[16])
  355. {
  356. struct sdp_string_parser_t t;
  357. struct sdp_string_token_t k;
  358. t.s = s;
  359. t.e = s + n;
  360. if (!sdp_get_token(&t, &k, " "))
  361. return -1;
  362. sdp_strcpy(orient, k.p, k.n);
  363. return 0;
  364. }
  365. /*
  366. * https://www.rfc-editor.org/rfc/rfc8851.html#name-sdp-arid-media-level-attrib
  367. * a=rid:<rid-id> <direction> [pt=<fmt-list>;]<restriction>=<value>...
  368. * a=rid:5 send pt=99,102;max-br=64000;max-width=1280;max-height=720;max-fps=30
  369. */
  370. int sdp_a_rid(const char* s, int n, struct sdp_rid_t* rid)
  371. {
  372. int i, len;
  373. struct sdp_string_parser_t t;
  374. struct sdp_string_token_t id, dir;
  375. t.s = s;
  376. t.e = s + n;
  377. if (!sdp_get_token(&t, &id, " ") || id.n >= sizeof(rid->rid)
  378. || !sdp_get_token(&t, &dir, " ") || dir.n >= sizeof(rid->direction))
  379. return -1;
  380. sdp_strcpy(rid->rid, id.p, id.n);
  381. sdp_strcpy(rid->direction, dir.p, dir.n);
  382. rid->params = sdp_string_split(t.s, (int)(intptr_t)(t.e - t.s), ";", &rid->nparam);
  383. for (i = 0; i < rid->nparam; i++)
  384. {
  385. len = (int)strlen(rid->params[i]);
  386. if (len > 10 && 0 == strncmp("max-width=", rid->params[i], 10))
  387. rid->width = atoi(rid->params[i] + 10);
  388. else if (len > 11 && 0 == strncmp("max-height=", rid->params[i], 11))
  389. rid->height = atoi(rid->params[i] + 11);
  390. else if (len > 8 && 0 == strncmp("max-fps=", rid->params[i], 8))
  391. rid->fps = atoi(rid->params[i] + 8);
  392. else if (len > 7 && 0 == strncmp("max-fs=", rid->params[i], 7))
  393. rid->fs = atoi(rid->params[i] + 7);
  394. else if (len > 7 && 0 == strncmp("max-br=", rid->params[i], 7))
  395. rid->br = atoi(rid->params[i] + 7);
  396. else if (len > 8 && 0 == strncmp("max-pps=", rid->params[i], 8))
  397. rid->pps = atoi(rid->params[i] + 8);
  398. else if (len > 8 && 0 == strncmp("max-bpp=", rid->params[i], 8))
  399. rid->bpp = atof(rid->params[i] + 8);
  400. else if (len > 7 && 0 == strncmp("depend=", rid->params[i], 7))
  401. rid->depends = sdp_string_split(rid->params[i] + 7, len, ",", &rid->ndepend);
  402. else if (len > 3 && 0 == strncmp("pt=", rid->params[i], 3))
  403. rid->payloads = sdp_string_split(rid->params[i] + 3, len, ",", &rid->npayload);
  404. }
  405. return rid->rid ? 0 : -1;
  406. }
  407. /*
  408. * https://www.rfc-editor.org/rfc/rfc3605
  409. * rtcp-attribute = "a=rtcp:" port [nettype space addrtype space connection-address] CRLF
  410. * a=rtcp:53020
  411. * a=rtcp:53020 IN IP6 2001:2345:6789:ABCD:EF01:2345:6789:ABCD
  412. */
  413. int sdp_a_rtcp(const char* s, int n, struct sdp_address_t* address)
  414. {
  415. int port;
  416. struct sdp_string_parser_t t;
  417. struct sdp_string_token_t k, net, addr, conn;
  418. t.s = s;
  419. t.e = s + n;
  420. if (!sdp_get_token(&t, &k, " "))
  421. return -1;
  422. if (0 != sdp_str2int(k.p, k.n, &port))
  423. return -1;
  424. address->port[1] = address->port[0] = port; // copy
  425. if (sdp_get_token(&t, &net, " ") && sdp_get_token(&t, &addr, " ") && sdp_get_token(&t, &conn, " ")
  426. && net.n < sizeof(address->network) && addr.n < sizeof(address->addrtype) && conn.n < sizeof(address->address))
  427. {
  428. sdp_strcpy(address->network, net.p, net.n);
  429. sdp_strcpy(address->addrtype, addr.p, addr.n);
  430. sdp_strcpy(address->address, conn.p, conn.n);
  431. }
  432. return 0;
  433. }
  434. /*
  435. * https://www.rfc-editor.org/rfc/rfc8859.html#name-rtcp-fb-attribute-analysis
  436. * rtcp-fb-syntax = "a=rtcp-fb:" rtcp-fb-pt SP rtcp-fb-val CRLF
  437. rtcp-fb-pt = "*" ; wildcard: applies to all formats
  438. / fmt ; as defined in SDP spec
  439. rtcp-fb-val = "ack" rtcp-fb-ack-param
  440. / "nack" rtcp-fb-nack-param
  441. / "trr-int" SP 1*DIGIT
  442. / rtcp-fb-id rtcp-fb-param
  443. rtcp-fb-id = 1*(alpha-numeric / "-" / "_")
  444. rtcp-fb-param = SP "app" [SP byte-string]
  445. / SP token [SP byte-string]
  446. / ; empty
  447. rtcp-fb-ack-param = SP "rpsi"
  448. / SP "app" [SP byte-string]
  449. / SP token [SP byte-string]
  450. / ; empty
  451. rtcp-fb-nack-param = SP "pli"
  452. / SP "sli"
  453. / SP "rpsi"
  454. / SP "app" [SP byte-string]
  455. / SP token [SP byte-string]
  456. / ; empty
  457. */
  458. int sdp_a_rtcp_fb(const char* s, int n, struct sdp_rtcp_fb_t* fb)
  459. {
  460. struct sdp_string_parser_t t;
  461. struct sdp_string_token_t fmt, k, v;
  462. t.s = s;
  463. t.e = s + n;
  464. if (!sdp_get_token(&t, &fmt, " ") || !sdp_get_token(&t, &k, " ") || k.n >= sizeof(fb->feedback))
  465. return -1;
  466. if (fmt.n == 1 && '*' == *fmt.p)
  467. fb->fmt = -1;
  468. else if (0 != sdp_str2int(fmt.p, fmt.n, &fb->fmt))
  469. return -1;
  470. sdp_strcpy(fb->feedback, k.p, k.n);
  471. if (sdp_get_token(&t, &v, " ") && v.n < sizeof(fb->param))
  472. {
  473. sdp_strcpy(fb->param, v.p, v.n);
  474. if (3 == k.n && 0 == strncmp(k.p, "ack", 3))
  475. {
  476. }
  477. else if (4 == k.n && 0 == strncmp(k.p, "nack", 4))
  478. {
  479. }
  480. else if (7 == k.n && 0 == strncmp(k.p, "trr-int", 7))
  481. {
  482. if (0 != sdp_str2int(v.p, v.n, &fb->trr_int))
  483. return -1;
  484. }
  485. else
  486. {
  487. }
  488. }
  489. return 0;
  490. }
  491. /*
  492. * https://www.rfc-editor.org/rfc/rfc8853.html#name-detailed-description
  493. * a=simulcast:recv 1;2 send 4
  494. sc-value = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] )
  495. sc-send = %s"send" SP sc-str-list
  496. sc-recv = %s"recv" SP sc-str-list
  497. sc-str-list = sc-alt-list *( ";" sc-alt-list )
  498. sc-alt-list = sc-id *( "," sc-id )
  499. sc-id-paused = "~"
  500. sc-id = [sc-id-paused] rid-id
  501. ; SP defined in [RFC5234]
  502. ; rid-id defined in [RFC8851]
  503. */
  504. int sdp_a_simulcast(const char* s, int n, struct sdp_simulcast_t* simulcast)
  505. {
  506. char** pk;
  507. int i, num, direction;
  508. struct sdp_string_parser_t t;
  509. struct sdp_string_token_t k, v;
  510. t.s = s;
  511. t.e = s + n;
  512. simulcast->nrecv = simulcast->nsend = 0;
  513. simulcast->recvs = simulcast->sends = NULL;
  514. for (i = 0; i < 2; i++)
  515. {
  516. if (!sdp_get_token(&t, &k, " ") || !sdp_get_token(&t, &v, " "))
  517. break;
  518. if (4 == k.n && 0 == memcmp(k.p, "send", 4))
  519. direction = 1;
  520. else if (4 == k.n && 0 == memcmp(k.p, "recv", 4))
  521. direction = 2;
  522. else
  523. continue;
  524. pk = sdp_string_split(v.p, v.n, ";", &num);
  525. if (1 == direction)
  526. {
  527. simulcast->sends = pk;
  528. simulcast->nsend = num;
  529. }
  530. else if (2 == direction)
  531. {
  532. simulcast->recvs = pk;
  533. simulcast->nrecv = num;
  534. }
  535. else
  536. {
  537. assert(0);
  538. }
  539. }
  540. return 0;
  541. }
  542. /*
  543. * https://www.rfc-editor.org/rfc/rfc5576.html#section-4.1
  544. * a=ssrc:<ssrc-id> <attribute>
  545. * a=ssrc:<ssrc-id> <attribute>:<value>
  546. */
  547. int sdp_a_ssrc(const char* s, int n, uint32_t* ssrc, char attribute[64], char value[128])
  548. {
  549. int u;
  550. struct sdp_string_parser_t t;
  551. struct sdp_string_token_t id, k, v;
  552. t.s = s;
  553. t.e = s + n;
  554. if (!sdp_get_token(&t, &id, " ") || !sdp_get_token(&t, &k, ":") || k.n >= 64)
  555. return -1;
  556. if (0 != sdp_str2int(id.p, id.n, &u))
  557. return -1;
  558. *ssrc = (uint32_t)u;
  559. sdp_strcpy(attribute, k.p, k.n);
  560. if(sdp_get_token(&t, &v, "\r\n") && v.n < 128)
  561. sdp_strcpy(value, v.p, v.n);
  562. else
  563. *value = '\0';
  564. return 0;
  565. }
  566. /*
  567. * https://www.rfc-editor.org/rfc/rfc5576.html#section-4.2
  568. * a=ssrc-group:<semantics> <ssrc-id> ...
  569. *
  570. * a=ssrc-group:FID 3720618323 2524685008
  571. * a=ssrc:3720618323 cname:VQFSeIb012G+W2KA
  572. * a=ssrc:3720618323 msid:LCMSv0 LCMSv0
  573. * a=ssrc:3720618323 mslabel:LCMSv0
  574. * a=ssrc:3720618323 label:LCMSv0
  575. * a=ssrc:2524685008 cname:VQFSeIb012G+W2KA
  576. * a=ssrc:2524685008 msid:LCMSv0 LCMSv0
  577. * a=ssrc:2524685008 mslabel:LCMSv0
  578. * a=ssrc:2524685008 label:LCMSv0
  579. */
  580. int sdp_a_ssrc_group(const char* s, int n, struct sdp_ssrc_group_t* group)
  581. {
  582. struct sdp_string_parser_t t;
  583. struct sdp_string_token_t k;
  584. t.s = s;
  585. t.e = s + n;
  586. if (!sdp_get_token(&t, &k, " ") || k.n >= sizeof(group->key))
  587. return -1;
  588. sdp_strcpy(group->key, k.p, k.n);
  589. group->values = sdp_string_split(t.s, (int)(intptr_t)(t.e - t.s), " ", &group->count);
  590. return group->key ? 0 : -1;
  591. }
  592. #if defined(DEBUG) || defined(_DEBUG)
  593. static void sdp_a_extmap_test(void)
  594. {
  595. char url[128];
  596. int ext, direction;
  597. const char* s = "6/recvonly http://www.webrtc.org/experiments/rtp-hdrext/playout-delay";
  598. assert(0 == sdp_a_extmap(s, strlen(s), &ext, &direction, url));
  599. assert(6 == ext && SDP_A_RECVONLY == direction && 0 == strcmp("http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", url));
  600. s = "7 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
  601. assert(0 == sdp_a_extmap(s, strlen(s), &ext, &direction, url));
  602. assert(7 == ext && SDP_A_SENDRECV == direction && 0 == strcmp("http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", url));
  603. }
  604. static void sdp_a_simulcast_test(void)
  605. {
  606. struct sdp_simulcast_t simulcast;
  607. memset(&simulcast, 0, sizeof(simulcast));
  608. sdp_a_simulcast("recv 1;2 send 4", 15, &simulcast);
  609. assert(2 == simulcast.nrecv && 1 == simulcast.nsend && 0 == strcmp(simulcast.recvs[0], "1") && 0 == strcmp(simulcast.recvs[1], "2") && 0 == strcmp(simulcast.sends[0], "4"));
  610. free(simulcast.recvs); free(simulcast.sends);
  611. memset(&simulcast, 0, sizeof(simulcast));
  612. sdp_a_simulcast("send 4 recv 1;2", 15, &simulcast);
  613. assert(2 == simulcast.nrecv && 1 == simulcast.nsend && 0 == strcmp(simulcast.recvs[0], "1") && 0 == strcmp(simulcast.recvs[1], "2") && 0 == strcmp(simulcast.sends[0], "4"));
  614. free(simulcast.recvs); free(simulcast.sends);
  615. }
  616. static void sdp_a_ssrc_test(void)
  617. {
  618. uint32_t ssrc;
  619. char attr[64], value[128];
  620. assert(0 == sdp_a_ssrc("123456 key", 10, &ssrc, attr, value) && ssrc == 123456 && 0 == strcmp(attr, "key") && !*value);
  621. assert(0 == sdp_a_ssrc("123456 key:value", 16, &ssrc, attr, value) && ssrc == 123456 && 0 == strcmp(attr, "key") && 0 == strcmp(value, "value"));
  622. assert(0 == sdp_a_ssrc("3720618323 msid:LCMSv0 LCMSv0", 29, &ssrc, attr, value) && ssrc == 3720618323 && 0 == strcmp(attr, "msid") && 0 == strcmp(value, "LCMSv0 LCMSv0"));
  623. assert(0 != sdp_a_ssrc("123456f key:value", 17, &ssrc, attr, value));
  624. }
  625. static void sdp_a_ssrc_group_test(void)
  626. {
  627. struct sdp_ssrc_group_t group;
  628. memset(&group, 0, sizeof(group));
  629. assert(0 == sdp_a_ssrc_group("FID 3720618323 2524685008", 25, &group) && 0 == strcmp(group.key, "FID") && 2 == group.count && 0 == strcmp(group.values[0], "3720618323") && 0 == strcmp(group.values[1], "2524685008"));
  630. free(group.values);
  631. }
  632. static void sdp_a_rtcp_test(void)
  633. {
  634. struct sdp_address_t addr;
  635. memset(&addr, 0, sizeof(addr));
  636. assert(0 == sdp_a_rtcp("53020", 5, &addr) && 53020 == addr.port[0]);
  637. assert(0 == sdp_a_rtcp("53020 IN IP6 2001:2345:6789:ABCD:EF01:2345:6789:ABCD", 52, &addr) && 53020 == addr.port[0] && 0 == strcmp(addr.network, "IN") && 0 == strcmp(addr.addrtype, "IP6") && 0 == strcmp(addr.address, "2001:2345:6789:ABCD:EF01:2345:6789:ABCD"));
  638. }
  639. static void sdp_a_rid_test(void)
  640. {
  641. struct sdp_rid_t rid;
  642. memset(&rid, 0, sizeof(rid));
  643. assert(0 == sdp_a_rid("5 send", 6, &rid) && 0 == strcmp("5", rid.rid) && 0 == strcmp("send", rid.direction) && 0 == rid.nparam && 0 == rid.npayload && 0 == rid.ndepend);
  644. assert(0 == sdp_a_rid("5 send pt=99,102;max-br=64000;max-width=1280;max-height=720;max-fps=30", 70, &rid) && 0 == strcmp("5", rid.rid) && 0 == strcmp("send", rid.direction) && 2 == rid.npayload && 0 == strcmp("99", rid.payloads[0]) && 0 == strcmp("102", rid.payloads[1]) && 5 == rid.nparam && 64000 == rid.br && 1280 == rid.width && 720 == rid.height && 30 == rid.fps && 0 == rid.ndepend);
  645. free(rid.params);
  646. }
  647. static void sdp_a_fingerprint_test(void)
  648. {
  649. char hash[16];
  650. char fingerprint[128];
  651. assert(0 == sdp_a_fingerprint("sha-256 47:5A:AC:E6:8B:47:2A:A8:BC:AE:08:0D:E2:11:31:A5:38:F7:AE:F6:89:07:40:6C:CB:CC:99:CF:5F:C5:B8:4F", 103, hash, fingerprint) && 0 == strcmp("sha-256", hash) && 0 == strcmp("47:5A:AC:E6:8B:47:2A:A8:BC:AE:08:0D:E2:11:31:A5:38:F7:AE:F6:89:07:40:6C:CB:CC:99:CF:5F:C5:B8:4F", fingerprint));
  652. }
  653. static void sdp_a_msid_test(void)
  654. {
  655. char msid[65];
  656. char appdata[65];
  657. assert(0 == sdp_a_msid("5jbA0s68fQKkMHiM7ZDVxoVOox1mXyMvC7bR 22ceddc2-d518-4ed4-b9ad-708c87720561", 73, msid, appdata) && 0 == strcmp("5jbA0s68fQKkMHiM7ZDVxoVOox1mXyMvC7bR", msid) && 0 == strcmp("22ceddc2-d518-4ed4-b9ad-708c87720561", appdata));
  658. }
  659. static void sdp_a_mid_test(void)
  660. {
  661. char tag[256];
  662. assert(0 == sdp_a_mid("audio", 5, tag) && 0 == strcmp("audio", tag));
  663. }
  664. static void sdp_a_group_test(void)
  665. {
  666. int count;
  667. char** groups;
  668. char semantics[32];
  669. assert(0 == sdp_a_group("LS 1 2", 6, semantics, &groups, &count) && 0 == strcmp("LS", semantics) && 2 == count && 0 == strcmp("1", groups[0]) && 0 == strcmp("2", groups[1]));
  670. free(groups);
  671. assert(0 == sdp_a_group("BUNDLE audio video", 18, semantics, &groups, &count) && 0 == strcmp("BUNDLE", semantics) && 2 == count && 0 == strcmp("audio", groups[0]) && 0 == strcmp("video", groups[1]));
  672. free(groups);
  673. }
  674. static void sdp_a_ice_candidate_test(void)
  675. {
  676. struct sdp_ice_candidate_t c;
  677. memset(&c, 0, sizeof(c));
  678. assert(0 == sdp_a_ice_candidate("2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr 203.0.113.141 rport 8998", 75, &c)
  679. && 0 == strcmp(c.foundation, "2") && 1 == c.component && 0 == strcmp(c.transport, "UDP") && 1694498815 == c.priority
  680. && 0 == strcmp(c.address, "192.0.2.3") && 45664 == c.port && 0 == strcmp(c.candtype, "srflx") && 0 == strcmp(c.reladdr, "203.0.113.141") && 8998 == c.relport && 4 == c.nextension);
  681. memset(&c, 0, sizeof(c));
  682. assert(0 == sdp_a_ice_candidate("1 1 udp 2013266431 47.95.197.19 50858 typ host generation 0", 59, &c)
  683. && 0 == strcmp(c.foundation, "1") && 1 == c.component && 0 == strcmp(c.transport, "udp") && 2013266431 == c.priority
  684. && 0 == strcmp(c.address, "47.95.197.19") && 50858 == c.port && 0 == strcmp(c.candtype, "host") && 0 == strcmp(c.reladdr, "") && 0 == c.relport
  685. && 2 == c.nextension && 0 == strcmp("generation", c.extensions[0]) && 0 == strcmp("0", c.extensions[1]) && 0 == c.generation);
  686. free(c.extensions);
  687. }
  688. static void sdp_a_ice_remote_candidates_test(void)
  689. {
  690. struct sdp_ice_candidate_t c;
  691. memset(&c, 0, sizeof(c));
  692. assert(0 == sdp_a_ice_remote_candidates("1 192.0.2.3 45664", 17, &c) && 1 == c.component && 0 == strcmp("192.0.2.3", c.address) && 45664 == c.port);
  693. }
  694. static void sdp_a_rtcp_fb_test(void)
  695. {
  696. struct sdp_rtcp_fb_t fb;
  697. memset(&fb, 0, sizeof(fb));
  698. assert(0 == sdp_a_rtcp_fb("111 nack", 8, &fb) && 111 == fb.fmt && 0 == strcmp("nack", fb.feedback) && !*fb.param);
  699. assert(0 == sdp_a_rtcp_fb("96 nack pli", 11, &fb) && 96 == fb.fmt && 0 == strcmp("nack", fb.feedback) && 0 == strcmp("pli", fb.param));
  700. assert(0 == sdp_a_rtcp_fb("96 ccm fir", 10, &fb) && 96 == fb.fmt && 0 == strcmp("ccm", fb.feedback) && 0 == strcmp("fir", fb.param));
  701. }
  702. void sdp_a_webrtc_test(void)
  703. {
  704. sdp_a_extmap_test();
  705. sdp_a_fingerprint_test();
  706. sdp_a_group_test();
  707. sdp_a_ice_candidate_test();
  708. sdp_a_ice_remote_candidates_test();
  709. sdp_a_mid_test();
  710. sdp_a_msid_test();
  711. sdp_a_rid_test();
  712. sdp_a_rtcp_test();
  713. sdp_a_rtcp_fb_test();
  714. sdp_a_simulcast_test();
  715. sdp_a_ssrc_test();
  716. sdp_a_ssrc_group_test();
  717. }
  718. #endif