1 /*
2 This file is part of FreeSDP
3 Copyright (C) 2001,2002,2003 Federico Montesino Pouzols <fedemp@altern.org>
4
5 FreeSDP is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Benjamin Zores, (C) 2006
20 added support in parser for the a=control: lines.
21 added support in parser for the a=range: lines.
22 */
23
24 /**
25 * @file
26 *
27 * @short Parsing module implementation.
28 *
29 * This file implements the parsing routine <code>fsdp_parse</code>
30 * and the <code>fsdp_get_xxxx</code> routines that allow to get the
31 * session properties from a session description object build through
32 * the application of <code>fsdp_parse</code> to a textual SDP session
33 * description.
34 **/
35
36 #include "parserpriv.h"
37
38 /**
39 * \brief find the start of the next line
40 * \param c pointer to current position in string
41 * \return pointer to start of next line or NULL if illegal (i.e.
42 * a '\r' is not followed by a '\n'
43 */
next_line(const char * c)44 static const char *next_line(const char *c) {
45 c += strcspn(c, "\n\r");
46 if (*c == 0) return c;
47 if (*c == '\r') c++;
48 if (*c == '\n')
49 return c + 1;
50 return NULL;
51 }
52
53 /**
54 * Moves the <code>c<code> pointer up to the beginning of the next
55 * line.
56 *
57 * @param c char pointer to pointer
58 * @retval FSDPE_ILLEGAL_CHARACTER, when an illegal '\r' character
59 * (not followed by a '\n') is found, returns
60 */
61 #define NEXT_LINE(c) do { if (!(c = next_line(c))) return FSDPE_ILLEGAL_CHARACTER; } while (0);
62
63 fsdp_error_t
fsdp_parse(const char * text_description,fsdp_description_t * dsc)64 fsdp_parse (const char *text_description, fsdp_description_t * dsc)
65 {
66 fsdp_error_t result;
67 const char *p = text_description, *p2;
68 unsigned int j;
69 /* temps for sscanf */
70 #define TEMPCHARS 6
71 char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN];
72 char longfsdp_buf[MAXLONGFIELDLEN];
73 #define TEMPINTS 2
74 unsigned long int wuint[TEMPINTS];
75
76 if ((NULL == text_description) || (NULL == dsc))
77 return FSDPE_INVALID_PARAMETER;
78
79 /***************************************************************************/
80 /* A) parse session-level description */
81 /***************************************************************************/
82
83 /* `v=' line (protocol version) */
84 /* according to the RFC, only `v=0' is valid */
85 if (sscanf (p, "v=%1lu", &wuint[0]))
86 {
87 if (wuint[0] != 0)
88 return FSDPE_INVALID_VERSION;
89 }
90 else
91 {
92 return FSDPE_MISSING_VERSION;
93 }
94 NEXT_LINE (p);
95
96 /* `o=' line (owner/creator and session identifier) */
97 /* o=<username> <session id> <version> <network type> <address type>
98 <address> */
99 if (!strncmp (p, "o=", 2))
100 {
101 p += 2;
102 /* note that the following max lengths may vary in the future and
103 are quite arbitary */
104 if (sscanf
105 (p,
106 "%" MSFLENS "[\x21-\xFF] %" MSFLENS "[0-9] %" MSFLENS
107 "[0-9] %2s %3s %" MSFLENS "s", fsdp_buf[0], fsdp_buf[1],
108 fsdp_buf[2], fsdp_buf[3], fsdp_buf[4], fsdp_buf[5]) != 6)
109 return FSDPE_INVALID_OWNER;
110 dsc->o_username = strdup (fsdp_buf[0]);
111 dsc->o_session_id = strdup (fsdp_buf[1]);
112 dsc->o_announcement_version = strdup (fsdp_buf[2]);
113 if (!strncmp (fsdp_buf[3], "IN", 2))
114 {
115 dsc->o_network_type = FSDP_NETWORK_TYPE_INET;
116 if (!strncmp (fsdp_buf[4], "IP4", 3))
117 dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV4;
118 else if (!strncmp (fsdp_buf[4], "IP6", 3))
119 dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV6;
120 else
121 return FSDPE_INVALID_OWNER;
122 }
123 else
124 {
125 return FSDPE_INVALID_OWNER;
126 }
127 /* TODO? check valid unicast address/FQDN */
128 dsc->o_address = strdup (fsdp_buf[5]);
129 }
130 else
131 {
132 return FSDPE_MISSING_OWNER;
133 }
134 NEXT_LINE (p);
135
136 /* `s=' line (session name) -note that the name string cannot be empty */
137 /* s=<session name> */
138 if (!strncmp (p, "s=", 2))
139 {
140 if (sscanf (p, "s=%" MLFLENS "[^\r\n]", longfsdp_buf) < 1)
141 return FSDPE_EMPTY_NAME;
142 dsc->s_name = strdup (longfsdp_buf);
143 }
144 else
145 {
146 return FSDPE_MISSING_NAME;
147 }
148 NEXT_LINE (p);
149
150 /* `i=' line (session information) [optional] */
151 /* i=<session description> */
152 if (!strncmp (p, "i=", 2)
153 && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf))
154 {
155 dsc->i_information = strdup (longfsdp_buf);
156 NEXT_LINE (p);
157 }
158 else
159 {
160 /* (optional) information absent */
161 }
162
163 /* `u=' line (URI of description) [optional] */
164 /* u=<URI> */
165 if (!strncmp (p, "u=", 2)
166 && sscanf (p, "u=%" MLFLENS "[^\r\n]", longfsdp_buf))
167 {
168 /* TODO? check valid uri */
169 dsc->u_uri = strdup (longfsdp_buf);
170 NEXT_LINE (p);
171 }
172 else
173 {
174 /* (optional) uri absent */
175 }
176
177 /* `e=' lines (email address) [zero or more] */
178 /* e=<email address> */
179 p2 = p;
180 j = 0;
181 while (!strncmp (p2, "e=", 2))
182 {
183 /* First, count how many emails are there */
184 j++;
185 NEXT_LINE (p2);
186 }
187 dsc->emails_count = j;
188 if (dsc->emails_count > 0)
189 {
190 /* Then, build the array of emails */
191 dsc->emails = calloc (j, sizeof (const char *));
192 for (j = 0; j < dsc->emails_count; j++)
193 {
194 sscanf (p, "e=%" MLFLENS "[^\r\n]", longfsdp_buf);
195 /* TODO? check valid email-address. */
196 dsc->emails[j] = strdup (longfsdp_buf);
197 NEXT_LINE (p);
198 }
199 }
200
201 /* `p=' lines (phone number) [zero or more] */
202 /* p=<phone number> */
203 j = 0;
204 /* assert ( p2 == p ); */
205 while (!strncmp (p2, "p=", 2))
206 {
207 j++;
208 NEXT_LINE (p2);
209 }
210 dsc->phones_count = j;
211 if (dsc->phones_count > 0)
212 {
213 dsc->phones = calloc (j, sizeof (const char *));
214 for (j = 0; j < dsc->phones_count; j++)
215 {
216 sscanf (p, "p=%" MLFLENS "[^\r\n]", longfsdp_buf);
217 /* TODO? check valid phone-number. */
218 dsc->phones[j] = strdup (longfsdp_buf);
219 NEXT_LINE (p);
220 }
221 }
222
223 /* `c=' line (connection information - not required if included in all media) [optional] */
224 /* c=<network type> <address type> <connection address> */
225 result = fsdp_parse_c (&p, &(dsc->c_network_type), &(dsc->c_address_type),
226 &(dsc->c_address));
227 if (FSDPE_OK != result)
228 return result;
229
230 /* `b=' lines (bandwidth information) [optional] */
231 /* b=<modifier>:<bandwidth-value> */
232 result =
233 fsdp_parse_b (&p, &(dsc->bw_modifiers), &(dsc->bw_modifiers_count));
234 if (FSDPE_OK != result)
235 return result;
236
237 /* A.1) Time descriptions: */
238
239 /* `t=' lines (time the session is active) [1 or more] */
240 /* t=<start time> <stop time> */
241 j = 0;
242 p2 = p;
243 while (!strncmp (p2, "t=", 2))
244 {
245 j++;
246 NEXT_LINE (p2);
247 while (!strncmp (p2, "r=", 2))
248 NEXT_LINE (p2);
249 }
250 dsc->time_periods_count = j;
251 if (dsc->time_periods_count == 0)
252 return FSDPE_MISSING_TIME;
253 dsc->time_periods = calloc (dsc->time_periods_count,
254 sizeof (fsdp_time_period_t *));
255 for (j = 0; j < dsc->time_periods_count; j++)
256 {
257 unsigned int h = 0;
258 if (sscanf (p, "t=%10lu %10lu", &wuint[0], &wuint[1]) != 2)
259 {
260 /* not all periods have been successfully parsed */
261 dsc->time_periods_count = j;
262 return FSDPE_INVALID_TIME;
263 }
264 dsc->time_periods[j] = calloc (1, sizeof (fsdp_time_period_t));
265
266 /* convert from NTP to time_t time */
267 if (wuint[0] != 0)
268 wuint[0] -= NTP_EPOCH_OFFSET;
269 if (wuint[1] != 0)
270 wuint[1] -= NTP_EPOCH_OFFSET;
271 dsc->time_periods[j]->start = wuint[0];
272 dsc->time_periods[j]->stop = wuint[1];
273 NEXT_LINE (p);
274
275 /* `r' lines [zero or more repeat times for each t=] */
276 /*r=<repeat interval> <active duration> <list of offsets from
277 start-time> */
278 p2 = p;
279 while (!strncmp (p2, "r=", 2))
280 {
281 h++;
282 NEXT_LINE (p2);
283 }
284 dsc->time_periods[j]->repeats_count = h;
285 if (h > 0)
286 {
287 unsigned int index2 = 0;
288 dsc->time_periods[j]->repeats =
289 calloc (h, sizeof (fsdp_repeat_t *));
290 for (h = 0; h < dsc->time_periods[j]->repeats_count; h++)
291 {
292 /*
293 get_repeat_values(p,&(dsc->time_periods[index].repeats[index2]));
294 fsdp_error_t get_repeat_values (const char *r, fsdp_repeat_t
295 *repeat);
296 */
297 if (sscanf (p, "r=%10s %10s %" MLFLENS "[^\r\n]",
298 fsdp_buf[0], fsdp_buf[1], longfsdp_buf) == 3)
299 {
300 fsdp_repeat_t *repeat;
301 dsc->time_periods[j]->repeats[h] =
302 calloc (1, sizeof (fsdp_repeat_t));
303 repeat = dsc->time_periods[j]->repeats[h];
304 /* get interval, duration and list of offsets */
305 result =
306 fsdp_repeat_time_to_uint (fsdp_buf[0],
307 &(repeat->interval));
308 if (result == FSDPE_OK)
309 {
310 result =
311 fsdp_repeat_time_to_uint (fsdp_buf[1],
312 &(repeat->duration));
313 if (result == FSDPE_OK)
314 {
315 unsigned int k = 1;
316 const char *i = longfsdp_buf;
317 while (NULL != (i = strchr (i, ' ')))
318 {
319 k++;
320 if (NULL != i)
321 i++;
322 }
323 repeat->offsets_count = k;
324 repeat->offsets = calloc (k, sizeof (time_t));
325 i = longfsdp_buf;
326 for (k = 0;
327 (k < repeat->offsets_count)
328 && (result == FSDPE_OK) && i; k++)
329 {
330 result =
331 fsdp_repeat_time_to_uint (i,
332 &(repeat->
333 offsets[k]));
334 i = strchr (i, ' ');
335 if (NULL != i)
336 i++;
337 }
338 if (k < repeat->offsets_count)
339 {
340 /* there where invalid repeat offsets */
341 dsc->time_periods[j]->repeats_count = k;
342 return FSDPE_INVALID_REPEAT;
343 }
344 }
345 }
346 if (result != FSDPE_OK)
347 {
348 /* not all repeats have been succesfully parsed */
349 dsc->time_periods[j]->repeats_count = h;
350 return FSDPE_INVALID_REPEAT;
351 }
352 NEXT_LINE (p);
353 }
354 else
355 {
356 /* not all repeats have been succesfully parsed */
357 dsc->time_periods[j]->repeats_count = h;
358 return FSDPE_INVALID_REPEAT;
359 }
360 index2++;
361 }
362 }
363 }
364
365 /* `z=' line (time zone adjustments) [zero or more] */
366 /* z=<adjustment time> <offset> <adjustment time> <offset> .... */
367 if (!strncmp (p, "z=", 2))
368 {
369 if (sscanf (p, "z=%" MLFLENS "[^\r\n]", longfsdp_buf))
370 {
371 /* TODO: guess how many pairs are there and process them */
372 dsc->timezone_adj = strdup (longfsdp_buf);
373 NEXT_LINE (p);
374 }
375 else
376 {
377 return FSDPE_INVALID_TIMEZONE;
378 }
379 }
380
381 /* `k=' line (encryption key) [optional] */
382 /* k=<method>
383 k=<method>:<encryption key> */
384 result = fsdp_parse_k (&p, &(dsc->k_encryption_method),
385 &(dsc->k_encryption_content));
386 if (result != FSDPE_OK)
387 return result;
388
389 /* A.2) Attributes */
390 /* `a=' lines (session attribute) [0 or more] */
391 /* a=<attribute>
392 a=<attribute>:<value> */
393 while (!strncmp (p, "a=", 2))
394 {
395 /* The "9" length specifier of the first string is subject to
396 changes */
397 if (sscanf
398 (p, "a=%9[^:\r\n]:%" MSFLENS "[^\r\n]", fsdp_buf[0],
399 fsdp_buf[1]) == 2)
400 {
401 /* session-level value attributes */
402 if (!strncmp (fsdp_buf[0], "cat", 3))
403 dsc->a_category = strdup (fsdp_buf[1]);
404 else if (!strncmp (fsdp_buf[0], "keywds", 6))
405 dsc->a_keywords = strdup (fsdp_buf[1]);
406 else if (!strncmp (fsdp_buf[0], "tool", 4))
407 dsc->a_keywords = strdup (fsdp_buf[1]);
408 else if (!strncmp (fsdp_buf[0], "rtpmap", 6))
409 fsdp_parse_rtpmap (&(dsc->a_rtpmaps),
410 &(dsc->a_rtpmaps_count), fsdp_buf[1]);
411 else if (!strncmp (fsdp_buf[0], "type", 4))
412 {
413 if (!strncmp (fsdp_buf[1], "broadcast", 9))
414 dsc->a_type = FSDP_SESSION_TYPE_BROADCAST;
415 else if (!strncmp (fsdp_buf[1], "meeting", 7))
416 dsc->a_type = FSDP_SESSION_TYPE_MEETING;
417 else if (!strncmp (fsdp_buf[1], "moderated", 9))
418 dsc->a_type = FSDP_SESSION_TYPE_MODERATED;
419 else if (!strncmp (fsdp_buf[1], "test", 4))
420 dsc->a_type = FSDP_SESSION_TYPE_TEST;
421 else if (!strncmp (fsdp_buf[1], "H332", 4))
422 dsc->a_type = FSDP_SESSION_TYPE_H332;
423 else
424 return FSDPE_INVALID_SESSION_TYPE;
425 }
426 else if (!strncmp (fsdp_buf[0], "charset", 7))
427 dsc->a_charset = strdup (fsdp_buf[1]);
428 else if (!strncmp (fsdp_buf[0], "sdplang", 7))
429 {
430 if (NULL == dsc->a_sdplangs)
431 {
432 dsc->a_sdplangs_count = 0;
433 dsc->a_sdplangs =
434 calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
435 }
436 if (dsc->a_sdplangs_count < SDPLANGS_MAX_COUNT)
437 {
438 dsc->a_sdplangs[dsc->a_sdplangs_count] =
439 strdup (fsdp_buf[1]);
440 dsc->a_sdplangs_count++;
441 }
442 }
443 else if (!strncmp (fsdp_buf[0], "lang", 4))
444 {
445 if (NULL == dsc->a_langs)
446 {
447 dsc->a_langs_count = 0;
448 dsc->a_langs = calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
449 }
450 if (dsc->a_langs_count < SDPLANGS_MAX_COUNT)
451 {
452 dsc->a_langs[dsc->a_langs_count] = strdup (fsdp_buf[1]);
453 dsc->a_langs_count++;
454 }
455 }
456 else if (!strncmp (fsdp_buf[0], "control", 7))
457 {
458 if (NULL == dsc->a_controls)
459 {
460 dsc->a_controls_count = 0;
461 dsc->a_controls =
462 calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *));
463 }
464 if (dsc->a_controls_count < SDPCONTROLS_MAX_COUNT)
465 {
466 dsc->a_controls[dsc->a_controls_count] =
467 strdup (fsdp_buf[1]);
468 dsc->a_controls_count++;
469 }
470 }
471 else if (!strncmp (fsdp_buf[0], "range", 5))
472 {
473 free (dsc->a_range);
474 dsc->a_range = strdup (fsdp_buf[1]);
475 }
476 else
477 {
478 /* ignore unknown attributes, but provide access to them */
479 *longfsdp_buf = '\0';
480 strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN-1);
481 strncat (longfsdp_buf, ":", MAXLONGFIELDLEN-strlen(longfsdp_buf)-1);
482 strncat (longfsdp_buf, fsdp_buf[1], MAXLONGFIELDLEN-strlen(longfsdp_buf)-1);
483 if (NULL == dsc->unidentified_attributes)
484 {
485 dsc->unidentified_attributes_count = 0;
486 dsc->unidentified_attributes =
487 calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
488 sizeof (char *));
489 }
490 if (dsc->unidentified_attributes_count <
491 UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
492 {
493 dsc->unidentified_attributes
494 [dsc->unidentified_attributes_count] =
495 strdup (longfsdp_buf);
496 dsc->unidentified_attributes_count++;
497 }
498 }
499 NEXT_LINE (p);
500 }
501 else if (sscanf (p, "a=%20s", fsdp_buf[0]) == 1)
502 {
503 /* session-level property attributes */
504 if (!strncmp (fsdp_buf[0], "recvonly", 8))
505 dsc->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY;
506 else if (!strncmp (fsdp_buf[0], "sendonly", 8))
507 dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY;
508 else if (!strncmp (fsdp_buf[0], "inactive", 8))
509 dsc->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE;
510 else if (!strncmp (fsdp_buf[0], "sendrecv", 8))
511 dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV;
512 else
513 {
514 /* ignore unknown attributes, but provide access to them */
515 *longfsdp_buf = '\0';
516 strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN-1);
517 if (NULL == dsc->unidentified_attributes)
518 {
519 dsc->unidentified_attributes_count = 0;
520 dsc->unidentified_attributes =
521 calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
522 sizeof (char *));
523 }
524 if (dsc->unidentified_attributes_count <
525 UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
526 {
527 dsc->unidentified_attributes
528 [dsc->unidentified_attributes_count] =
529 strdup (longfsdp_buf);
530 dsc->unidentified_attributes_count++;
531 }
532 }
533 NEXT_LINE (p);
534 }
535 else
536 return FSDPE_INVALID_ATTRIBUTE;
537 }
538
539 /***************************************************************************/
540 /* B) parse media-level descriptions */
541 /***************************************************************************/
542 p2 = p;
543 j = 0;
544 while ((*p2 != '\0') && !strncmp (p2, "m=", 2))
545 {
546 char c;
547 j++;
548 NEXT_LINE (p2);
549 while (sscanf (p2, "%c=", &c) == 1)
550 {
551 if (c == 'i' || c == 'c' || c == 'b' || c == 'k' || c == 'a')
552 {
553 NEXT_LINE (p2);
554 }
555 else if (c == 'm')
556 {
557 break;
558 }
559 else
560 {
561 return FSDPE_INVALID_LINE;
562 }
563 }
564 }
565 dsc->media_announcements_count = j;
566 if (dsc->media_announcements_count == 0)
567 {
568 ;
569 /*return FSDPE_MISSING_MEDIA; */
570 }
571 else
572 { /* dsc->media_announcements_count > 0 */
573 dsc->media_announcements =
574 calloc (j, sizeof (fsdp_media_announcement_t *));
575 for (j = 0; j < dsc->media_announcements_count; j++)
576 {
577 fsdp_media_announcement_t *media = NULL;
578 /* `m=' line (media name, transport address and format list) */
579 /* m=<media> <port> <transport> <fmt list> */
580 /* The max. string lengths are subject to change */
581 if (sscanf (p, "m=%11s %8s %7s %" MLFLENS "[^\r\n]",
582 fsdp_buf[0], fsdp_buf[1], fsdp_buf[2],
583 longfsdp_buf) != 4)
584 {
585 return FSDPE_INVALID_MEDIA;
586 }
587 else
588 {
589 dsc->media_announcements[j] =
590 calloc (1, sizeof (fsdp_media_announcement_t));
591 media = dsc->media_announcements[j];
592 if (!strncmp (fsdp_buf[0], "audio", 5))
593 media->media_type = FSDP_MEDIA_AUDIO;
594 else if (!strncmp (fsdp_buf[0], "video", 5))
595 media->media_type = FSDP_MEDIA_VIDEO;
596 else if (!strncmp (fsdp_buf[0], "application", 11))
597 media->media_type = FSDP_MEDIA_APPLICATION;
598 else if (!strncmp (fsdp_buf[0], "data", 4))
599 media->media_type = FSDP_MEDIA_DATA;
600 else if (!strncmp (fsdp_buf[0], "control", 7))
601 media->media_type = FSDP_MEDIA_CONTROL;
602 else
603 return FSDPE_UNKNOWN_MEDIA_TYPE;
604 { /* try to get port specification as port/number */
605 char *slash;
606 if ((slash = strchr (fsdp_buf[1], '/')))
607 {
608 *slash = '\0';
609 slash++;
610 media->port = strtol (fsdp_buf[1], NULL, 10);
611 media->port_count = strtol (slash, NULL, 10);
612 }
613 else
614 {
615 media->port = strtol (fsdp_buf[1], NULL, 10);
616 media->port_count = 0;
617 }
618 }
619 if (!strncmp (fsdp_buf[2], "RTP/AVP", 7))
620 media->transport = FSDP_TP_RTP_AVP;
621 else if (!strncmp (fsdp_buf[2], "udp", 3))
622 media->transport = FSDP_TP_UDP;
623 else if (!strncmp (fsdp_buf[2], "TCP", 3))
624 media->transport = FSDP_TP_TCP;
625 else if (!strncmp (fsdp_buf[2], "UDPTL", 5))
626 media->transport = FSDP_TP_UDPTL;
627 else if (!strncmp (fsdp_buf[2], "vat", 3))
628 media->transport = FSDP_TP_VAT;
629 else if (!strncmp (fsdp_buf[2], "rtp", 3))
630 media->transport = FSDP_TP_OLD_RTP;
631 else
632 return FSDPE_UNKNOWN_MEDIA_TRANSPORT;
633 {
634 unsigned int k = 0;
635 char *s = longfsdp_buf;
636 while (NULL != (s = strchr (s, ' ')))
637 {
638 k++;
639 if (NULL != s)
640 s++;
641 }
642 k++; /* when there is no space left, count the last format */
643 media->formats_count = k;
644 media->formats = calloc (k, sizeof (char *));
645 s = longfsdp_buf;
646 for (k = 0; k < media->formats_count; k++)
647 {
648 char *space = strchr (s, ' ');
649 if (NULL != space)
650 *space = '\0';
651 media->formats[k] = strdup (s);
652 s = space + 1;
653 }
654 }
655 NEXT_LINE (p);
656 }
657
658 /* `i=' line (media title) [optional] */
659 /* i=<media title> */
660 if (!strncmp (p, "i=", 2)
661 && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf))
662 {
663 media->i_title = strdup (longfsdp_buf);
664 NEXT_LINE (p);
665 }
666 else
667 {
668 /* (optional) information absent */
669 }
670
671 /* `c=' line (connection information - overrides session-level
672 line) [optional if provided at session-level] */
673 /* c=<network type> <address type> <connection address> */
674 result = fsdp_parse_c (&p, &(media->c_network_type),
675 &(media->c_address_type),
676 &(media->c_address));
677 if (result != FSDPE_OK)
678 return result;
679
680 /* `b=' lines (bandwidth information) [optional] */
681 /* b=<modifier>:<bandwidth-value> */
682 result = fsdp_parse_b (&p, &(media->bw_modifiers),
683 &(media->bw_modifiers_count));
684 if (FSDPE_OK != result)
685 return result;
686
687 /* `k=' line (encryption key) [optional] */
688 /* k=<method>
689 k=<method>:<encryption key> */
690 result = fsdp_parse_k (&p, &(media->k_encryption_method),
691 &(media->k_encryption_content));
692 if (result != FSDPE_OK)
693 return result;
694
695 /* B.1) Attributes */
696
697 /* `a=' lines (zero or more media attribute lines) [optional] */
698 /* a=<attribute>
699 a=<attribute>:<value> */
700 while (!strncmp (p, "a=", 2))
701 {
702 if (sscanf
703 (p, "a=%9[^:\r\n]:%" MLFLENS "[^\r\n]", fsdp_buf[0],
704 longfsdp_buf) == 2)
705 {
706 /* media-level value attributes */
707 if (!strncmp (fsdp_buf[0], "ptime", 5))
708 media->a_ptime = strtoul (longfsdp_buf, NULL, 10);
709 else if (!strncmp (fsdp_buf[0], "maxptime", 8))
710 media->a_maxptime = strtoul (longfsdp_buf, NULL, 10);
711 else if (!strncmp (fsdp_buf[0], "rtpmap", 6))
712 fsdp_parse_rtpmap (&(media->a_rtpmaps),
713 &(media->a_rtpmaps_count),
714 longfsdp_buf);
715 else if (!strncmp (fsdp_buf[0], "orient", 6))
716 {
717 if (!strncmp (longfsdp_buf, "portrait", 8))
718 media->a_orient = FSDP_ORIENT_PORTRAIT;
719 else if (!strncmp (longfsdp_buf, "landscape", 9))
720 media->a_orient = FSDP_ORIENT_LANDSCAPE;
721 else if (!strncmp (longfsdp_buf, "seascape", 9))
722 media->a_orient = FSDP_ORIENT_SEASCAPE;
723 }
724 else if (!strncmp (fsdp_buf[0], "sdplang", 7))
725 {
726 if (NULL == dsc->a_sdplangs)
727 {
728 media->a_sdplangs_count = 0;
729 media->a_sdplangs =
730 calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
731 }
732 if (media->a_sdplangs_count < SDPLANGS_MAX_COUNT)
733 {
734 media->a_sdplangs[dsc->a_sdplangs_count] =
735 strdup (longfsdp_buf);
736 media->a_sdplangs_count++;
737 }
738 }
739 else if (!strncmp (fsdp_buf[0], "lang", 4))
740 {
741 if (NULL == dsc->a_langs)
742 {
743 media->a_langs_count = 0;
744 media->a_langs =
745 calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
746 }
747 if (media->a_langs_count < SDPLANGS_MAX_COUNT)
748 {
749 media->a_langs[dsc->a_langs_count] =
750 strdup (longfsdp_buf);
751 media->a_langs_count++;
752 }
753 }
754 else if (!strncmp (fsdp_buf[0], "control", 7))
755 {
756 if (NULL == media->a_controls)
757 {
758 media->a_controls_count = 0;
759 media->a_controls =
760 calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *));
761 }
762 if (media->a_controls_count < SDPCONTROLS_MAX_COUNT)
763 {
764 media->a_controls[media->a_controls_count] =
765 strdup (longfsdp_buf);
766 media->a_controls_count++;
767 }
768 }
769 else if (!strncmp (fsdp_buf[0], "range", 5))
770 {
771 free (media->a_range);
772 media->a_range = strdup (fsdp_buf[1]);
773 }
774 else if (!strncmp (fsdp_buf[0], "framerate", 9))
775 media->a_framerate = strtod (longfsdp_buf, NULL);
776 else if (!strncmp (fsdp_buf[0], "fmtp", 4))
777 {
778 if (NULL == media->a_fmtps)
779 {
780 media->a_fmtps_count = 0;
781 media->a_fmtps =
782 calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
783 }
784 if (media->a_fmtps_count < SDPLANGS_MAX_COUNT)
785 {
786 media->a_fmtps[media->a_fmtps_count] =
787 strdup (longfsdp_buf);
788 media->a_fmtps_count++;
789 }
790 }
791 else if (!strncmp (fsdp_buf[0], "rtcp", 4))
792 {
793 int opts = 0;
794 /* rtcp attribute: a=rtcp:<port> <nettype> <addrtype> <address> */
795 opts =
796 sscanf (longfsdp_buf, "%lu %2s %3s %" MSFLENS "s",
797 &wuint[0], fsdp_buf[0], fsdp_buf[1],
798 fsdp_buf[2]);
799 if (opts >= 1)
800 {
801 media->a_rtcp_port = wuint[0];
802 if (opts >= 2)
803 {
804 if (!strncmp (fsdp_buf[0], "IN", 2))
805 {
806 media->a_rtcp_network_type =
807 FSDP_NETWORK_TYPE_INET;
808 } /* else
809 ; TODO: define error code? */
810 if (opts >= 3)
811 {
812 if (!strncmp (fsdp_buf[1], "IP4", 3))
813 media->a_rtcp_address_type =
814 FSDP_ADDRESS_TYPE_IPV4;
815 else if (!strncmp (fsdp_buf[1], "IP6", 3))
816 media->a_rtcp_address_type =
817 FSDP_ADDRESS_TYPE_IPV6;
818 else
819 return FSDPE_INVALID_CONNECTION_NETTYPE;
820 /*add specific code? */
821 if (opts >= 4)
822 media->a_rtcp_address =
823 strdup (fsdp_buf[2]);
824 }
825 }
826 }
827 }
828 else
829 {
830 /* ignore unknown attributes, but provide access to them */
831 *fsdp_buf[1] = '\0';
832 strncat (fsdp_buf[1], fsdp_buf[0], MAXSHORTFIELDLEN-1);
833 strncat (fsdp_buf[1], ":", MAXSHORTFIELDLEN-strlen(fsdp_buf[1])-1);
834 strncat (fsdp_buf[1], longfsdp_buf, MAXSHORTFIELDLEN-strlen(fsdp_buf[1])-1);
835 if (NULL == media->unidentified_attributes)
836 {
837 media->unidentified_attributes_count = 0;
838 media->unidentified_attributes =
839 calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
840 sizeof (char *));
841 }
842 if (media->unidentified_attributes_count <
843 UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
844 {
845 media->unidentified_attributes
846 [media->unidentified_attributes_count] =
847 strdup (fsdp_buf[1]);
848 media->unidentified_attributes_count++;
849 }
850 }
851 NEXT_LINE (p);
852 }
853 else if (sscanf (p, "a=%8s", fsdp_buf[0]) == 1)
854 {
855 /* media-level property attributes */
856 if (!strncmp (fsdp_buf[0], "recvonly", 8))
857 media->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY;
858 else if (!strncmp (fsdp_buf[0], "sendonly", 8))
859 media->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY;
860 else if (!strncmp (fsdp_buf[0], "inactive", 8))
861 media->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE;
862 else if (!strncmp (fsdp_buf[0], "sendrecv", 8))
863 media->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV;
864 else
865 {
866 /* ignore unknown attributes, but provide access to them */
867 *longfsdp_buf = '\0';
868 strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN-1);
869 if (NULL == media->unidentified_attributes)
870 {
871 media->unidentified_attributes_count = 0;
872 media->unidentified_attributes =
873 calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
874 sizeof (char *));
875 }
876 if (media->unidentified_attributes_count <
877 UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
878 {
879 media->unidentified_attributes
880 [media->unidentified_attributes_count] =
881 strdup (longfsdp_buf);
882 media->unidentified_attributes_count++;
883 }
884 }
885 NEXT_LINE (p);
886 }
887 else
888 return FSDPE_INVALID_ATTRIBUTE;
889 }
890 } /* end of for */
891 }
892
893 /* Check c= has been given at session level or at media level for
894 all media */
895 if (NULL == dsc->c_address.address)
896 {
897 unsigned int c;
898 for (c = 0; c < dsc->media_announcements_count; c++)
899 if (NULL == dsc->media_announcements[c]->c_address.address)
900 return FSDPE_MISSING_CONNECTION_INFO;
901 }
902
903 /* finish */
904 if (*p == '\0')
905 return FSDPE_OK;
906 else
907 return FSDPE_OVERFILLED;
908 }
909
910 static fsdp_error_t
fsdp_parse_c(const char ** p,fsdp_network_type_t * ntype,fsdp_address_type_t * atype,fsdp_connection_address_t * address)911 fsdp_parse_c (const char **p, fsdp_network_type_t * ntype,
912 fsdp_address_type_t * atype,
913 fsdp_connection_address_t * address)
914 {
915 #undef TEMPCHARS
916 #define TEMPCHARS 3
917 char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN];
918
919 if (!strncmp (*p, "c=", 2))
920 {
921 if (sscanf (*p, "c=%2s %3s %" MSFLENS "s",
922 fsdp_buf[0], fsdp_buf[1], fsdp_buf[2]))
923 {
924 if (!strncmp (fsdp_buf[0], "IN", 2))
925 {
926 *ntype = FSDP_NETWORK_TYPE_INET;
927 if (!strncmp (fsdp_buf[1], "IP4", 3))
928 *atype = FSDP_ADDRESS_TYPE_IPV4;
929 else if (!strncmp (fsdp_buf[1], "IP6", 3))
930 *atype = FSDP_ADDRESS_TYPE_IPV6;
931 else
932 return FSDPE_INVALID_CONNECTION_NETTYPE;
933 }
934 else
935 {
936 return FSDPE_INVALID_CONNECTION_ADDRTYPE;
937 }
938 {
939 char *slash = strchr (fsdp_buf[2], '/');
940 if (NULL == slash)
941 {
942 address->address = strdup (fsdp_buf[2]);
943 address->address_ttl = 0;
944 address->address_count = 0;
945 }
946 else
947 {
948 /* address is IP4 multicast */
949 char *slash2;
950 *slash = '\0';
951 slash++;
952 address->address = strdup (fsdp_buf[2]);
953 slash2 = strchr (slash + 1, '/');
954 if (NULL == slash2)
955 {
956 address->address_ttl = strtol (slash, NULL, 10);
957 address->address_count = 0;
958 }
959 else
960 {
961 *slash2 = '\0';
962 slash2++;
963 address->address_ttl = strtol (slash, NULL, 10);
964 address->address_count = strtol (slash2, NULL, 10);
965 }
966 }
967 }
968 NEXT_LINE (*p);
969 }
970 else
971 {
972 return FSDPE_INVALID_CONNECTION;
973 }
974 }
975 return FSDPE_OK;
976 }
977
978 static fsdp_error_t
fsdp_parse_b(const char ** p,fsdp_bw_modifier_t ** bw_modifiers,unsigned int * bw_modifiers_count)979 fsdp_parse_b (const char **p, fsdp_bw_modifier_t ** bw_modifiers,
980 unsigned int *bw_modifiers_count)
981 {
982 char fsdp_buf[MAXSHORTFIELDLEN];
983 unsigned long int wuint;
984 unsigned int i = 0;
985 const char *lp = *p;
986
987 /* count b= lines */
988 while (!strncmp (lp, "b=", 2))
989 {
990 NEXT_LINE (lp);
991 i++;
992 }
993 *bw_modifiers = calloc (i, sizeof (fsdp_bw_modifier_t));
994 *bw_modifiers_count = i;
995
996 while (i > 0)
997 {
998 unsigned int index = *bw_modifiers_count - i;
999 if (2 == sscanf (*p, "b=%20[^:\r\n]:%lu", fsdp_buf, &wuint))
1000 {
1001 if (!strncmp (fsdp_buf, "CT", 2))
1002 (*bw_modifiers)[index].b_mod_type =
1003 FSDP_BW_MOD_TYPE_CONFERENCE_TOTAL;
1004 else if (!strncmp (fsdp_buf, "AS", 2))
1005 (*bw_modifiers)[index].b_mod_type =
1006 FSDP_BW_MOD_TYPE_APPLICATION_SPECIFIC;
1007 else if (!strncmp (fsdp_buf, "RS", 2))
1008 (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_RTCP_SENDERS;
1009 else if (!strncmp (fsdp_buf, "RR", 2))
1010 (*bw_modifiers)[index].b_mod_type =
1011 FSDP_BW_MOD_TYPE_RTCP_RECEIVERS;
1012 else
1013 {
1014 (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_UNKNOWN;
1015 (*bw_modifiers)[index].b_unknown_bw_modt =
1016 (char *) strdup (fsdp_buf);
1017 }
1018 (*bw_modifiers)[index].b_value = wuint;
1019 NEXT_LINE (*p);
1020 }
1021 else
1022 {
1023 *bw_modifiers_count -= i;
1024 return FSDPE_INVALID_BANDWIDTH;
1025 }
1026 i--;
1027 }
1028 return FSDPE_OK;
1029 }
1030
1031 static fsdp_error_t
fsdp_parse_k(const char ** p,fsdp_encryption_method_t * method,char ** content)1032 fsdp_parse_k (const char **p, fsdp_encryption_method_t * method,
1033 char **content)
1034 {
1035 char fsdp_buf[MAXSHORTFIELDLEN];
1036 char longfsdp_buf[MAXLONGFIELDLEN];
1037
1038 if (!strncmp (*p, "k=", 2))
1039 {
1040 if (sscanf (*p, "k=prompt"))
1041 {
1042 *method = FSDP_ENCRYPTION_METHOD_PROMPT;
1043 *content = NULL;
1044 NEXT_LINE (*p);
1045 }
1046 else
1047 {
1048 if (sscanf
1049 (*p, "k=%6[^:\r\n]:%" MLFLENS "s", fsdp_buf, longfsdp_buf))
1050 {
1051 if (!strncmp (fsdp_buf, "clear", 5))
1052 *method = FSDP_ENCRYPTION_METHOD_CLEAR;
1053 else if (!strncmp (fsdp_buf, "base64", 6))
1054 *method = FSDP_ENCRYPTION_METHOD_BASE64;
1055 else if (!strncmp (fsdp_buf, "uri", 3))
1056 *method = FSDP_ENCRYPTION_METHOD_URI;
1057 else
1058 return FSDPE_INVALID_ENCRYPTION_METHOD;
1059 *content = strdup (longfsdp_buf);
1060 NEXT_LINE (*p);
1061 }
1062 }
1063 }
1064 return FSDPE_OK;
1065 }
1066
1067 static fsdp_error_t
fsdp_parse_rtpmap(fsdp_rtpmap_t *** rtpmap,unsigned int * counter,const char * value)1068 fsdp_parse_rtpmap (fsdp_rtpmap_t *** rtpmap, unsigned int *counter,
1069 const char *value)
1070 {
1071 fsdp_error_t result = FSDPE_OK;
1072
1073 if (0 == *counter)
1074 {
1075 *counter = 0;
1076 *rtpmap = calloc (MEDIA_RTPMAPS_MAX_COUNT, sizeof (fsdp_rtpmap_t *));
1077 }
1078 if (*counter < MEDIA_RTPMAPS_MAX_COUNT)
1079 {
1080 unsigned int c = *counter;
1081 fsdp_rtpmap_t **map = *rtpmap;
1082 char fsdp_buf[MAXSHORTFIELDLEN];
1083 char longfsdp_buf[MAXLONGFIELDLEN];
1084 map[c] = calloc (1, sizeof (fsdp_rtpmap_t));
1085
1086 /* a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding
1087 parameters]> */
1088 if (2 == sscanf (value, "%s %s", fsdp_buf, longfsdp_buf))
1089 {
1090 char *slash1;
1091 map[c]->pt = strdup (fsdp_buf);
1092 /* parse <encoding name>/<clock rate>[/<encoding parameters>] */
1093 slash1 = strchr (longfsdp_buf, '/');
1094 if (NULL == slash1)
1095 {
1096 result = FSDPE_INVALID_ATTRIBUTE_RTPMAP;
1097 }
1098 else
1099 {
1100 char *slash2;
1101 *slash1 = '\0';
1102 slash1++;
1103 map[c]->encoding_name = strdup (longfsdp_buf);
1104 slash2 = strchr (slash1, '/');
1105 if (NULL != slash2)
1106 {
1107 *slash2 = '\0';
1108 slash2++;
1109 map[c]->parameters = strdup (slash2);
1110 }
1111 map[c]->clock_rate = strtol (slash1, NULL, 10);
1112 }
1113 (*counter)++;
1114 }
1115 }
1116 return result;
1117 }
1118
1119 static fsdp_error_t
fsdp_repeat_time_to_uint(const char * time,unsigned long int * seconds)1120 fsdp_repeat_time_to_uint (const char *time, unsigned long int *seconds)
1121 {
1122 const unsigned long SECONDS_PER_DAY = 86400;
1123 const unsigned long SECONDS_PER_HOUR = 3600;
1124 const unsigned long SECONDS_PER_MINUTE = 60;
1125 char c;
1126 unsigned long int wuint;
1127
1128 if (sscanf (time, "%lu%c", &wuint, &c) == 2)
1129 {
1130 /* time with unit specification character */
1131 switch (c)
1132 {
1133 case 'd':
1134 *seconds = wuint * SECONDS_PER_DAY;
1135 break;
1136 case 'h':
1137 *seconds = wuint * SECONDS_PER_HOUR;
1138 break;
1139 case 'm':
1140 *seconds = wuint * SECONDS_PER_MINUTE;
1141 break;
1142 case 's':
1143 *seconds = wuint;
1144 break;
1145 default:
1146 return FSDPE_INVALID_REPEAT;
1147 break;
1148 }
1149 }
1150 else if (sscanf (time, "%lu", &wuint) == 1)
1151 {
1152 /* time without unit specification character */
1153 *seconds = wuint;
1154 }
1155 else
1156 {
1157 return FSDPE_INVALID_REPEAT;
1158 }
1159 return FSDPE_OK;
1160 }
1161
1162 unsigned int
fsdp_get_version(const fsdp_description_t * dsc)1163 fsdp_get_version (const fsdp_description_t * dsc)
1164 {
1165 if (!dsc)
1166 return 0;
1167 return dsc->version;
1168 }
1169
1170 const char *
fsdp_get_owner_username(const fsdp_description_t * dsc)1171 fsdp_get_owner_username (const fsdp_description_t * dsc)
1172 {
1173 if (!dsc)
1174 return NULL;
1175 return dsc->o_username;
1176 }
1177
1178 const char *
fsdp_get_session_id(const fsdp_description_t * dsc)1179 fsdp_get_session_id (const fsdp_description_t * dsc)
1180 {
1181 if (!dsc)
1182 return NULL;
1183 return dsc->o_session_id;
1184 }
1185
1186 const char *
fsdp_get_announcement_version(const fsdp_description_t * dsc)1187 fsdp_get_announcement_version (const fsdp_description_t * dsc)
1188 {
1189 if (!dsc)
1190 return NULL;
1191 return dsc->o_announcement_version;
1192 }
1193
1194 fsdp_network_type_t
fsdp_get_owner_network_type(const fsdp_description_t * dsc)1195 fsdp_get_owner_network_type (const fsdp_description_t * dsc)
1196 {
1197 if (!dsc)
1198 return FSDP_NETWORK_TYPE_UNDEFINED;
1199 return dsc->o_network_type;
1200 }
1201
1202 fsdp_address_type_t
fsdp_get_owner_address_type(const fsdp_description_t * dsc)1203 fsdp_get_owner_address_type (const fsdp_description_t * dsc)
1204 {
1205 if (!dsc)
1206 return FSDP_ADDRESS_TYPE_UNDEFINED;
1207 return dsc->o_address_type;
1208 }
1209
1210 const char *
fsdp_get_owner_address(const fsdp_description_t * dsc)1211 fsdp_get_owner_address (const fsdp_description_t * dsc)
1212 {
1213 if (!dsc)
1214 return NULL;
1215 return dsc->o_address;
1216 }
1217
1218 const char *
fsdp_get_name(const fsdp_description_t * dsc)1219 fsdp_get_name (const fsdp_description_t * dsc)
1220 {
1221 if (!dsc)
1222 return NULL;
1223 return dsc->s_name;
1224 }
1225
1226 const char *
fsdp_get_information(const fsdp_description_t * dsc)1227 fsdp_get_information (const fsdp_description_t * dsc)
1228 {
1229 if (!dsc)
1230 return NULL;
1231 return dsc->i_information;
1232 }
1233
1234 const char *
fsdp_get_uri(const fsdp_description_t * dsc)1235 fsdp_get_uri (const fsdp_description_t * dsc)
1236 {
1237 if (!dsc)
1238 return NULL;
1239 return dsc->u_uri;
1240 }
1241
1242 unsigned int
fsdp_get_emails_count(const fsdp_description_t * dsc)1243 fsdp_get_emails_count (const fsdp_description_t * dsc)
1244 {
1245 if (!dsc)
1246 return 0;
1247 return dsc->emails_count;
1248 }
1249
1250 const char *
fsdp_get_email(const fsdp_description_t * dsc,unsigned int index)1251 fsdp_get_email (const fsdp_description_t * dsc, unsigned int index)
1252 {
1253 if ((!dsc) || (index >= dsc->emails_count))
1254 return NULL;
1255 return dsc->emails[index];
1256 }
1257
1258 unsigned int
fsdp_get_phones_count(const fsdp_description_t * dsc)1259 fsdp_get_phones_count (const fsdp_description_t * dsc)
1260 {
1261 if (!dsc)
1262 return 0;
1263 return dsc->phones_count;
1264 }
1265
1266 const char *
fsdp_get_phone(const fsdp_description_t * dsc,unsigned int index)1267 fsdp_get_phone (const fsdp_description_t * dsc, unsigned int index)
1268 {
1269 if ((!dsc) || (index >= dsc->phones_count))
1270 return NULL;
1271 return dsc->phones[index];
1272 }
1273
1274 fsdp_network_type_t
fsdp_get_global_conn_network_type(const fsdp_description_t * dsc)1275 fsdp_get_global_conn_network_type (const fsdp_description_t * dsc)
1276 {
1277 if (!dsc)
1278 return FSDP_NETWORK_TYPE_UNDEFINED;
1279 return dsc->c_network_type;
1280 }
1281
1282 fsdp_address_type_t
fsdp_get_global_conn_address_type(const fsdp_description_t * dsc)1283 fsdp_get_global_conn_address_type (const fsdp_description_t * dsc)
1284 {
1285 if (!dsc)
1286 return FSDP_ADDRESS_TYPE_UNDEFINED;
1287 return dsc->c_address_type;
1288 }
1289
1290 const char *
fsdp_get_global_conn_address(const fsdp_description_t * dsc)1291 fsdp_get_global_conn_address (const fsdp_description_t * dsc)
1292 {
1293 if (!dsc)
1294 return NULL;
1295 return dsc->c_address.address;
1296 }
1297
1298 unsigned int
fsdp_get_global_conn_address_ttl(const fsdp_description_t * dsc)1299 fsdp_get_global_conn_address_ttl (const fsdp_description_t * dsc)
1300 {
1301 if (!dsc)
1302 return 0;
1303 return dsc->c_address.address_ttl;
1304 }
1305
1306 unsigned int
fsdp_get_global_conn_address_count(const fsdp_description_t * dsc)1307 fsdp_get_global_conn_address_count (const fsdp_description_t * dsc)
1308 {
1309 if (!dsc)
1310 return 0;
1311 return dsc->c_address.address_count;
1312 }
1313
1314 unsigned int
fsdp_get_bw_modifier_count(const fsdp_description_t * dsc)1315 fsdp_get_bw_modifier_count (const fsdp_description_t * dsc)
1316 {
1317 if (!dsc)
1318 return 0;
1319 return dsc->bw_modifiers_count;
1320 }
1321
1322 fsdp_bw_modifier_type_t
fsdp_get_bw_modifier_type(const fsdp_description_t * dsc,unsigned int index)1323 fsdp_get_bw_modifier_type (const fsdp_description_t * dsc, unsigned int index)
1324 {
1325 if ((!dsc) || (index >= dsc->bw_modifiers_count))
1326 return FSDP_BW_MOD_TYPE_UNDEFINED;
1327 return dsc->bw_modifiers[index].b_mod_type;
1328 }
1329
1330 const char *
fsdp_get_bw_modifier_type_unknown(const fsdp_description_t * dsc,unsigned int index)1331 fsdp_get_bw_modifier_type_unknown (const fsdp_description_t * dsc,
1332 unsigned int index)
1333 {
1334 if ((!dsc) || (index >= dsc->bw_modifiers_count) ||
1335 (dsc->bw_modifiers[index].b_mod_type != FSDP_BW_MOD_TYPE_UNKNOWN))
1336 return NULL;
1337 return dsc->bw_modifiers[index].b_unknown_bw_modt;
1338 }
1339
1340 unsigned long int
fsdp_get_bw_value(const fsdp_description_t * dsc,unsigned int index)1341 fsdp_get_bw_value (const fsdp_description_t * dsc, unsigned int index)
1342 {
1343 if ((!dsc) || (index >= dsc->bw_modifiers_count))
1344 return 0;
1345 return dsc->bw_modifiers[index].b_value;
1346 }
1347
1348 time_t
fsdp_get_period_start(const fsdp_description_t * dsc,unsigned int index)1349 fsdp_get_period_start (const fsdp_description_t * dsc, unsigned int index)
1350 {
1351 if ((!dsc) || (index >= dsc->time_periods_count))
1352 return 0;
1353 return dsc->time_periods[index]->start;
1354 }
1355
1356 time_t
fsdp_get_period_stop(const fsdp_description_t * dsc,unsigned int index)1357 fsdp_get_period_stop (const fsdp_description_t * dsc, unsigned int index)
1358 {
1359 if ((!dsc) || (index >= dsc->time_periods_count))
1360 return 0;
1361 return dsc->time_periods[index]->stop;
1362 }
1363
1364 unsigned int
fsdp_get_period_repeats_count(const fsdp_description_t * dsc,unsigned int index)1365 fsdp_get_period_repeats_count (const fsdp_description_t * dsc,
1366 unsigned int index)
1367 {
1368 if ((!dsc) || (index >= dsc->time_periods_count))
1369 return 0;
1370 return dsc->time_periods[index]->repeats_count;
1371 }
1372
1373 unsigned long int
fsdp_get_period_repeat_interval(const fsdp_description_t * dsc,unsigned int index,unsigned int rindex)1374 fsdp_get_period_repeat_interval (const fsdp_description_t * dsc,
1375 unsigned int index, unsigned int rindex)
1376 {
1377 if ((!dsc) || (index >= dsc->time_periods_count))
1378 return 0;
1379 return dsc->time_periods[index]->repeats[rindex]->interval;
1380 }
1381
1382 unsigned long int
fsdp_get_period_repeat_duration(const fsdp_description_t * dsc,unsigned int index,unsigned int rindex)1383 fsdp_get_period_repeat_duration (const fsdp_description_t * dsc,
1384 unsigned int index, unsigned int rindex)
1385 {
1386 if ((!dsc) || (index >= dsc->time_periods_count))
1387 return 0;
1388 return dsc->time_periods[index]->repeats[rindex]->duration;
1389 }
1390
1391 const unsigned long int *
fsdp_get_period_repeat_offsets(const fsdp_description_t * dsc,unsigned int index,unsigned int rindex)1392 fsdp_get_period_repeat_offsets (const fsdp_description_t * dsc,
1393 unsigned int index, unsigned int rindex)
1394 {
1395 if ((!dsc) || (index >= dsc->time_periods_count))
1396 return NULL;
1397 return dsc->time_periods[index]->repeats[rindex]->offsets;
1398 }
1399
1400 const char *
fsdp_get_timezone_adj(const fsdp_description_t * dsc)1401 fsdp_get_timezone_adj (const fsdp_description_t * dsc)
1402 {
1403 if (!dsc)
1404 return NULL;
1405 return dsc->timezone_adj;
1406 }
1407
1408 unsigned int
fsdp_get_unidentified_attribute_count(const fsdp_description_t * dsc)1409 fsdp_get_unidentified_attribute_count (const fsdp_description_t * dsc)
1410 {
1411 if (!dsc)
1412 return 0;
1413 return dsc->unidentified_attributes_count;
1414 }
1415
1416 const char *
fsdp_get_unidentified_attribute(const fsdp_description_t * dsc,unsigned int index)1417 fsdp_get_unidentified_attribute (const fsdp_description_t * dsc,
1418 unsigned int index)
1419 {
1420 if (!dsc || (index < dsc->unidentified_attributes_count))
1421 return NULL;
1422 return dsc->unidentified_attributes[index];
1423 }
1424
1425 fsdp_encryption_method_t
fsdp_get_encryption_method(const fsdp_description_t * dsc)1426 fsdp_get_encryption_method (const fsdp_description_t * dsc)
1427 {
1428 if (!dsc)
1429 return FSDP_ENCRYPTION_METHOD_UNDEFINED;
1430 return dsc->k_encryption_method;
1431 }
1432
1433 const char *
fsdp_get_encryption_content(const fsdp_description_t * dsc)1434 fsdp_get_encryption_content (const fsdp_description_t * dsc)
1435 {
1436 if (!dsc || (dsc->k_encryption_method == FSDP_ENCRYPTION_METHOD_UNDEFINED))
1437 return NULL;
1438 return dsc->k_encryption_content;
1439 }
1440
1441 const char *
fsdp_get_str_att(const fsdp_description_t * dsc,fsdp_session_str_att_t att)1442 fsdp_get_str_att (const fsdp_description_t * dsc, fsdp_session_str_att_t att)
1443 {
1444 /*TODO: change these individual attributes with a table, thus
1445 avoiding this slow switch */
1446 char *result;
1447
1448 if (!dsc)
1449 return NULL;
1450
1451 switch (att)
1452 {
1453 case FSDP_SESSION_STR_ATT_CATEGORY:
1454 result = dsc->a_category;
1455 break;
1456 case FSDP_SESSION_STR_ATT_KEYWORDS:
1457 result = dsc->a_keywords;
1458 break;
1459 case FSDP_SESSION_STR_ATT_TOOL:
1460 result = dsc->a_tool;
1461 break;
1462 case FSDP_SESSION_STR_ATT_CHARSET:
1463 result = dsc->a_charset;
1464 break;
1465 default:
1466 result = NULL;
1467 }
1468 return result;
1469 }
1470
1471 unsigned int
fsdp_get_sdplang_count(const fsdp_description_t * dsc)1472 fsdp_get_sdplang_count (const fsdp_description_t * dsc)
1473 {
1474 if (!dsc)
1475 return 0;
1476 return dsc->a_sdplangs_count;
1477 }
1478
1479 const char *
fsdp_get_sdplang(const fsdp_description_t * dsc,unsigned int index)1480 fsdp_get_sdplang (const fsdp_description_t * dsc, unsigned int index)
1481 {
1482 if ((!dsc) || (index >= dsc->a_sdplangs_count))
1483 return NULL;
1484 return dsc->a_sdplangs[index];
1485 }
1486
1487 unsigned int
fsdp_get_control_count(const fsdp_description_t * dsc)1488 fsdp_get_control_count (const fsdp_description_t * dsc)
1489 {
1490 if (!dsc)
1491 return 0;
1492 return dsc->a_controls_count;
1493 }
1494
1495 const char *
fsdp_get_control(const fsdp_description_t * dsc,unsigned int index)1496 fsdp_get_control (const fsdp_description_t * dsc, unsigned int index)
1497 {
1498 if ((!dsc) || (index >= dsc->a_controls_count))
1499 return NULL;
1500 return dsc->a_controls[index];
1501 }
1502
1503 const char *
fsdp_get_range(const fsdp_description_t * dsc)1504 fsdp_get_range (const fsdp_description_t * dsc)
1505 {
1506 return dsc->a_range;
1507 }
1508
1509 fsdp_sendrecv_mode_t
fsdp_get_sendrecv_mode(const fsdp_description_t * dsc)1510 fsdp_get_sendrecv_mode (const fsdp_description_t * dsc)
1511 {
1512 if (!dsc)
1513 return FSDP_SENDRECV_UNDEFINED;
1514 return dsc->a_sendrecv_mode;
1515 }
1516
1517 fsdp_session_type_t
fsdp_get_session_type(const fsdp_description_t * dsc)1518 fsdp_get_session_type (const fsdp_description_t * dsc)
1519 {
1520 if (!dsc)
1521 return FSDP_SESSION_TYPE_UNDEFINED;
1522 return dsc->a_type;
1523 }
1524
1525 unsigned int
fsdp_get_media_count(const fsdp_description_t * dsc)1526 fsdp_get_media_count (const fsdp_description_t * dsc)
1527 {
1528 if (!dsc)
1529 return 0;
1530 return dsc->media_announcements_count;
1531 }
1532
1533 const fsdp_media_description_t *
fsdp_get_media(const fsdp_description_t * dsc,unsigned int index)1534 fsdp_get_media (const fsdp_description_t * dsc, unsigned int index)
1535 {
1536 if ((index >= dsc->media_announcements_count))
1537 return NULL;
1538 return dsc->media_announcements[index];
1539 }
1540
1541 fsdp_media_t
fsdp_get_media_type(const fsdp_media_description_t * dsc)1542 fsdp_get_media_type (const fsdp_media_description_t * dsc)
1543 {
1544 if (!dsc)
1545 return FSDP_MEDIA_UNDEFINED;
1546 return dsc->media_type;
1547 }
1548
1549 unsigned int
fsdp_get_media_port(const fsdp_media_description_t * dsc)1550 fsdp_get_media_port (const fsdp_media_description_t * dsc)
1551 {
1552 if (!dsc)
1553 return 0;
1554 return dsc->port;
1555 }
1556
1557 unsigned int
fsdp_get_media_port_count(const fsdp_media_description_t * dsc)1558 fsdp_get_media_port_count (const fsdp_media_description_t * dsc)
1559 {
1560 if (!dsc)
1561 return 0;
1562 return dsc->port_count;
1563 }
1564
1565 fsdp_transport_protocol_t
fsdp_get_media_transport_protocol(const fsdp_media_description_t * dsc)1566 fsdp_get_media_transport_protocol (const fsdp_media_description_t * dsc)
1567 {
1568 if (!dsc)
1569 return FSDP_TP_UNDEFINED;
1570 return dsc->transport;
1571 }
1572
1573 unsigned int
fsdp_get_media_formats_count(const fsdp_media_description_t * dsc)1574 fsdp_get_media_formats_count (const fsdp_media_description_t * dsc)
1575 {
1576 if (!dsc)
1577 return 0;
1578 return dsc->formats_count;
1579 }
1580
1581 const char *
fsdp_get_media_format(const fsdp_media_description_t * dsc,unsigned int index)1582 fsdp_get_media_format (const fsdp_media_description_t * dsc,
1583 unsigned int index)
1584 {
1585 if (!dsc || (index < dsc->formats_count - 1))
1586 return NULL;
1587 return dsc->formats[index];
1588 }
1589
1590 const char *
fsdp_get_media_title(const fsdp_media_description_t * dsc)1591 fsdp_get_media_title (const fsdp_media_description_t * dsc)
1592 {
1593 if (!dsc)
1594 return NULL;
1595 return dsc->i_title;
1596 }
1597
1598 fsdp_network_type_t
fsdp_get_media_network_type(const fsdp_media_description_t * dsc)1599 fsdp_get_media_network_type (const fsdp_media_description_t * dsc)
1600 {
1601 if (!dsc)
1602 return FSDP_NETWORK_TYPE_UNDEFINED;
1603 return dsc->c_network_type;
1604 }
1605
1606 fsdp_address_type_t
fsdp_get_media_address_type(const fsdp_media_description_t * dsc)1607 fsdp_get_media_address_type (const fsdp_media_description_t * dsc)
1608 {
1609 if (!dsc)
1610 return FSDP_ADDRESS_TYPE_UNDEFINED;
1611 return dsc->c_address_type;
1612 }
1613
1614 const char *
fsdp_get_media_address(const fsdp_media_description_t * dsc)1615 fsdp_get_media_address (const fsdp_media_description_t * dsc)
1616 {
1617 if (!dsc)
1618 return NULL;
1619 return dsc->c_address.address;
1620 }
1621
1622 unsigned int
fsdp_get_media_address_ttl(const fsdp_media_description_t * mdsc)1623 fsdp_get_media_address_ttl (const fsdp_media_description_t * mdsc)
1624 {
1625 if (!mdsc)
1626 return 0;
1627 return mdsc->c_address.address_ttl;
1628 }
1629
1630 unsigned int
fsdp_get_media_address_count(const fsdp_media_description_t * mdsc)1631 fsdp_get_media_address_count (const fsdp_media_description_t * mdsc)
1632 {
1633 if (!mdsc)
1634 return 0;
1635 return mdsc->c_address.address_count;
1636 }
1637
1638 fsdp_bw_modifier_type_t
fsdp_get_media_bw_modifier_type(const fsdp_media_description_t * dsc,unsigned int index)1639 fsdp_get_media_bw_modifier_type (const fsdp_media_description_t * dsc,
1640 unsigned int index)
1641 {
1642 if (!dsc || (index >= dsc->bw_modifiers_count))
1643 return FSDP_BW_MOD_TYPE_UNDEFINED;
1644 return dsc->bw_modifiers[index].b_mod_type;
1645 }
1646
1647 const char *
fsdp_get_media_bw_modifier_type_unknown(const fsdp_media_description_t * dsc,unsigned int index)1648 fsdp_get_media_bw_modifier_type_unknown (const fsdp_media_description_t * dsc,
1649 unsigned int index)
1650 {
1651 if (!dsc || (index >= dsc->bw_modifiers_count) ||
1652 (FSDP_BW_MOD_TYPE_UNKNOWN != dsc->bw_modifiers[index].b_mod_type))
1653 return NULL;
1654 return dsc->bw_modifiers[index].b_unknown_bw_modt;
1655 }
1656
1657 unsigned long int
fsdp_get_media_bw_value(const fsdp_media_description_t * dsc,unsigned int index)1658 fsdp_get_media_bw_value (const fsdp_media_description_t * dsc,
1659 unsigned int index)
1660 {
1661 if (!dsc || (index >= dsc->bw_modifiers_count))
1662 return 0;
1663 return dsc->bw_modifiers[index].b_value;
1664 }
1665
1666 fsdp_encryption_method_t
fsdp_get_media_encryption_method(const fsdp_media_description_t * dsc)1667 fsdp_get_media_encryption_method (const fsdp_media_description_t * dsc)
1668 {
1669 if (!dsc)
1670 return FSDP_ENCRYPTION_METHOD_UNDEFINED;
1671 return dsc->k_encryption_method;
1672 }
1673
1674 const char *
fsdp_get_media_encryption_content(const fsdp_media_description_t * dsc)1675 fsdp_get_media_encryption_content (const fsdp_media_description_t * dsc)
1676 {
1677 if (!dsc)
1678 return NULL;
1679 return dsc->k_encryption_content;
1680 }
1681
1682 unsigned int
fsdp_get_media_ptime(const fsdp_media_description_t * dsc)1683 fsdp_get_media_ptime (const fsdp_media_description_t * dsc)
1684 {
1685 if (!dsc)
1686 return 0;
1687 return dsc->a_ptime;
1688 }
1689
1690 unsigned int
fsdp_get_media_maxptime(const fsdp_media_description_t * dsc)1691 fsdp_get_media_maxptime (const fsdp_media_description_t * dsc)
1692 {
1693 if (!dsc)
1694 return 0;
1695 return dsc->a_maxptime;
1696 }
1697
1698 unsigned int
fsdp_get_media_rtpmap_count(const fsdp_media_description_t * mdsc)1699 fsdp_get_media_rtpmap_count (const fsdp_media_description_t * mdsc)
1700 {
1701 if (!mdsc)
1702 return 0;
1703 return mdsc->a_rtpmaps_count;
1704 }
1705
1706 const char *
fsdp_get_media_rtpmap_payload_type(const fsdp_media_description_t * mdsc,unsigned int index)1707 fsdp_get_media_rtpmap_payload_type (const fsdp_media_description_t * mdsc,
1708 unsigned int index)
1709 {
1710 if (!mdsc || (index >= mdsc->a_rtpmaps_count))
1711 return NULL;
1712 return mdsc->a_rtpmaps[index]->pt;
1713 }
1714
1715 const char *
fsdp_get_media_rtpmap_encoding_name(const fsdp_media_description_t * mdsc,unsigned int index)1716 fsdp_get_media_rtpmap_encoding_name (const fsdp_media_description_t * mdsc,
1717 unsigned int index)
1718 {
1719 if (!mdsc || (index >= mdsc->a_rtpmaps_count))
1720 return NULL;
1721 return mdsc->a_rtpmaps[index]->encoding_name;
1722 }
1723
1724 unsigned int
fsdp_get_media_rtpmap_clock_rate(const fsdp_media_description_t * mdsc,unsigned int index)1725 fsdp_get_media_rtpmap_clock_rate (const fsdp_media_description_t * mdsc,
1726 unsigned int index)
1727 {
1728 if (!mdsc || (index >= mdsc->a_rtpmaps_count))
1729 return 0;
1730 return mdsc->a_rtpmaps[index]->clock_rate;
1731 }
1732
1733 const char *
fsdp_get_media_rtpmap_encoding_parameters(const fsdp_description_t * mdsc,unsigned int index)1734 fsdp_get_media_rtpmap_encoding_parameters (const fsdp_description_t * mdsc,
1735 unsigned int index)
1736 {
1737 if (!mdsc || (index >= mdsc->a_rtpmaps_count))
1738 return NULL;
1739 return mdsc->a_rtpmaps[index]->parameters;
1740 }
1741
1742 unsigned int
fsdp_get_media_sdplang_count(const fsdp_media_description_t * mdsc)1743 fsdp_get_media_sdplang_count (const fsdp_media_description_t * mdsc)
1744 {
1745 if (!mdsc)
1746 return 0;
1747 return mdsc->a_sdplangs_count;
1748 }
1749
1750 const char *
fsdp_get_media_sdplang(const fsdp_media_description_t * mdsc,unsigned int index)1751 fsdp_get_media_sdplang (const fsdp_media_description_t * mdsc,
1752 unsigned int index)
1753 {
1754 if (!mdsc || (index >= mdsc->a_sdplangs_count))
1755 return NULL;
1756 return mdsc->a_sdplangs[index];
1757 }
1758
1759 unsigned int
fsdp_get_media_lang_count(const fsdp_media_description_t * mdsc)1760 fsdp_get_media_lang_count (const fsdp_media_description_t * mdsc)
1761 {
1762 if (!mdsc)
1763 return 0;
1764 return mdsc->a_langs_count;
1765 }
1766
1767 const char *
fsdp_get_media_lang(const fsdp_media_description_t * mdsc,unsigned int index)1768 fsdp_get_media_lang (const fsdp_media_description_t * mdsc,
1769 unsigned int index)
1770 {
1771 if (!mdsc || (index >= mdsc->a_langs_count))
1772 return NULL;
1773 return mdsc->a_langs[index];
1774 }
1775
1776 unsigned int
fsdp_get_media_control_count(const fsdp_media_description_t * mdsc)1777 fsdp_get_media_control_count (const fsdp_media_description_t * mdsc)
1778 {
1779 if (!mdsc)
1780 return 0;
1781 return mdsc->a_controls_count;
1782 }
1783
1784 char *
fsdp_get_media_control(const fsdp_media_description_t * mdsc,unsigned int index)1785 fsdp_get_media_control (const fsdp_media_description_t * mdsc,
1786 unsigned int index)
1787 {
1788 if (!mdsc || (index >= mdsc->a_controls_count))
1789 return NULL;
1790 return mdsc->a_controls[index];
1791 }
1792
1793 char *
fsdp_get_media_range(const fsdp_media_description_t * mdsc)1794 fsdp_get_media_range (const fsdp_media_description_t * mdsc)
1795 {
1796 return mdsc->a_range;
1797 }
1798
1799 unsigned int
fsdp_get_media_fmtp_count(const fsdp_media_description_t * mdsc)1800 fsdp_get_media_fmtp_count (const fsdp_media_description_t * mdsc)
1801 {
1802 if (!mdsc)
1803 return 0;
1804 return mdsc->a_fmtps_count;
1805 }
1806
1807 const char *
fsdp_get_media_fmtp(const fsdp_media_description_t * mdsc,unsigned int index)1808 fsdp_get_media_fmtp (const fsdp_media_description_t * mdsc,
1809 unsigned int index)
1810 {
1811 if (!mdsc || (index >= mdsc->a_fmtps_count))
1812 return NULL;
1813 return mdsc->a_fmtps[index];
1814 }
1815
1816 fsdp_orient_t
fsdp_get_media_orient(const fsdp_media_description_t * dsc)1817 fsdp_get_media_orient (const fsdp_media_description_t * dsc)
1818 {
1819 if (!dsc)
1820 return FSDP_ORIENT_UNDEFINED;
1821 return dsc->a_orient;
1822 }
1823
1824 fsdp_sendrecv_mode_t
fsdp_get_media_sendrecv(const fsdp_media_description_t * dsc)1825 fsdp_get_media_sendrecv (const fsdp_media_description_t * dsc)
1826 {
1827 if (!dsc)
1828 return FSDP_SENDRECV_UNDEFINED;
1829 return dsc->a_sendrecv_mode;
1830 }
1831
1832 float
fsdp_get_media_framerate(const fsdp_media_description_t * dsc)1833 fsdp_get_media_framerate (const fsdp_media_description_t * dsc)
1834 {
1835 if (!dsc)
1836 return 0;
1837 return dsc->a_framerate;
1838 }
1839
1840 unsigned int
fsdp_get_media_quality(const fsdp_media_description_t * dsc)1841 fsdp_get_media_quality (const fsdp_media_description_t * dsc)
1842 {
1843 if (!dsc)
1844 return 0;
1845 return dsc->a_quality;
1846 }
1847
1848 unsigned int
fsdp_get_media_rtcp_port(const fsdp_media_description_t * dsc)1849 fsdp_get_media_rtcp_port (const fsdp_media_description_t * dsc)
1850 {
1851 if (!dsc)
1852 return 0;
1853 return dsc->a_rtcp_port;
1854 }
1855
1856 fsdp_network_type_t
fsdp_get_media_rtcp_network_type(const fsdp_media_description_t * dsc)1857 fsdp_get_media_rtcp_network_type (const fsdp_media_description_t * dsc)
1858 {
1859 if (!dsc)
1860 return FSDP_NETWORK_TYPE_UNDEFINED;
1861 return dsc->a_rtcp_network_type;
1862 }
1863
1864 fsdp_address_type_t
fsdp_get_media_rtcp_address_type(const fsdp_media_description_t * dsc)1865 fsdp_get_media_rtcp_address_type (const fsdp_media_description_t * dsc)
1866 {
1867 if (!dsc)
1868 return FSDP_ADDRESS_TYPE_UNDEFINED;
1869 return dsc->a_rtcp_address_type;
1870 }
1871
1872 const char *
fsdp_get_media_rtcp_address(const fsdp_media_description_t * dsc)1873 fsdp_get_media_rtcp_address (const fsdp_media_description_t * dsc)
1874 {
1875 if (!dsc)
1876 return NULL;
1877 return dsc->a_rtcp_address;
1878 }
1879
1880 unsigned int
fsdp_get_media_unidentified_attribute_count(const fsdp_media_description_t * mdsc)1881 fsdp_get_media_unidentified_attribute_count (const fsdp_media_description_t
1882 * mdsc)
1883 {
1884 if (!mdsc)
1885 return 0;
1886 return mdsc->unidentified_attributes_count;
1887 }
1888
1889 const char *
fsdp_get_media_unidentified_attribute(const fsdp_media_description_t * mdsc,unsigned int index)1890 fsdp_get_media_unidentified_attribute (const fsdp_media_description_t * mdsc,
1891 unsigned int index)
1892 {
1893 if (!mdsc || (index < mdsc->unidentified_attributes_count))
1894 return NULL;
1895 return mdsc->unidentified_attributes[index];
1896 }
1897