blob: 07ce3a27e136edc977ed9a86af07de7738727d89 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file sdp/media.c SDP Media
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <stdlib.h>
7#include <string.h>
8#include <re_types.h>
9#include <re_fmt.h>
10#include <re_mem.h>
11#include <re_mbuf.h>
12#include <re_list.h>
13#include <re_sa.h>
14#include <re_sdp.h>
15#include "sdp.h"
16
17
18static void destructor(void *arg)
19{
20 struct sdp_media *m = arg;
21 unsigned i;
22
23 list_flush(&m->lfmtl);
24 list_flush(&m->rfmtl);
25 list_flush(&m->rattrl);
26 list_flush(&m->lattrl);
27
28 if (m->le.list) {
29 m->disabled = true;
30 m->ench = NULL;
31 mem_ref(m);
32 return;
33 }
34
35 for (i=0; i<ARRAY_SIZE(m->protov); i++)
36 mem_deref(m->protov[i]);
37
38 list_unlink(&m->le);
39 mem_deref(m->name);
40 mem_deref(m->proto);
41 mem_deref(m->uproto);
42}
43
44
45static int media_alloc(struct sdp_media **mp, struct list *list)
46{
47 struct sdp_media *m;
48 int i;
49
50 m = mem_zalloc(sizeof(*m), destructor);
51 if (!m)
52 return ENOMEM;
53
54 list_append(list, &m->le, m);
55
56 m->ldir = SDP_SENDRECV;
57 m->rdir = SDP_SENDRECV;
58 m->dynpt = RTP_DYNPT_START;
59
60 sa_init(&m->laddr, AF_INET);
61 sa_init(&m->raddr, AF_INET);
62 sa_init(&m->laddr_rtcp, AF_INET);
63 sa_init(&m->raddr_rtcp, AF_INET);
64
65 for (i=0; i<SDP_BANDWIDTH_MAX; i++) {
66 m->lbwv[i] = -1;
67 m->rbwv[i] = -1;
68 }
69
70 *mp = m;
71
72 return 0;
73}
74
75
76/**
77 * Add a media line to an SDP Session
78 *
79 * @param mp Pointer to allocated SDP Media line object
80 * @param sess SDP Session
81 * @param name Media name
82 * @param port Port number
83 * @param proto Transport protocol
84 *
85 * @return 0 if success, otherwise errorcode
86 */
87int sdp_media_add(struct sdp_media **mp, struct sdp_session *sess,
88 const char *name, uint16_t port, const char *proto)
89{
90 struct sdp_media *m;
91 int err;
92
93 if (!sess || !name || !proto)
94 return EINVAL;
95
96 err = media_alloc(&m, &sess->lmedial);
97 if (err)
98 return err;
99
100 err = str_dup(&m->name, name);
101 err |= str_dup(&m->proto, proto);
102 if (err)
103 goto out;
104
105 sa_set_port(&m->laddr, port);
106
107 out:
108 if (err)
109 mem_deref(m);
110 else if (mp)
111 *mp = m;
112
113 return err;
114}
115
116
117/**
118 * Add a remote SDP media line to an SDP Session
119 *
120 * @param mp Pointer to allocated SDP Media line object
121 * @param sess SDP Session
122 * @param name Media name
123 * @param proto Transport protocol
124 *
125 * @return 0 if success, otherwise errorcode
126 */
127int sdp_media_radd(struct sdp_media **mp, struct sdp_session *sess,
128 const struct pl *name, const struct pl *proto)
129{
130 struct sdp_media *m;
131 int err;
132
133 if (!mp || !sess || !name || !proto)
134 return EINVAL;
135
136 err = media_alloc(&m, &sess->medial);
137 if (err)
138 return err;
139
140 m->disabled = true;
141
142 err = pl_strdup(&m->name, name);
143 err |= pl_strdup(&m->proto, proto);
144
145 if (err)
146 mem_deref(m);
147 else
148 *mp = m;
149
150 return err;
151}
152
153
154/**
155 * Reset the remote part of an SDP Media line
156 *
157 * @param m SDP Media line
158 */
159void sdp_media_rreset(struct sdp_media *m)
160{
161 int i;
162
163 if (!m)
164 return;
165
166 sa_init(&m->raddr, AF_INET);
167 sa_init(&m->raddr_rtcp, AF_INET);
168
169 list_flush(&m->rfmtl);
170 list_flush(&m->rattrl);
171
172 m->rdir = SDP_SENDRECV;
173
174 for (i=0; i<SDP_BANDWIDTH_MAX; i++)
175 m->rbwv[i] = -1;
176}
177
178
179/**
180 * Compare media line protocols
181 *
182 * @param m SDP Media line
183 * @param proto Transport protocol
184 * @param update Update media protocol if match is found in alternate set
185 *
186 * @return True if matching, False if not
187 */
188bool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto,
189 bool update)
190{
191 unsigned i;
192
193 if (!m || !proto)
194 return false;
195
196 if (!pl_strcmp(proto, m->proto))
197 return true;
198
199 for (i=0; i<ARRAY_SIZE(m->protov); i++) {
200
201 if (!m->protov[i] || pl_strcmp(proto, m->protov[i]))
202 continue;
203
204 if (update) {
205 mem_deref(m->proto);
206 m->proto = mem_ref(m->protov[i]);
207 }
208
209 return true;
210 }
211
212 return false;
213}
214
215
216/**
217 * Find an SDP Media line from name and transport protocol
218 *
219 * @param sess SDP Session
220 * @param name Media name
221 * @param proto Transport protocol
222 * @param update_proto Update media transport protocol
223 *
224 * @return Matching media line if found, NULL if not found
225 */
226struct sdp_media *sdp_media_find(const struct sdp_session *sess,
227 const struct pl *name,
228 const struct pl *proto,
229 bool update_proto)
230{
231 struct le *le;
232
233 if (!sess || !name || !proto)
234 return NULL;
235
236 for (le=sess->lmedial.head; le; le=le->next) {
237
238 struct sdp_media *m = le->data;
239
240 if (pl_strcmp(name, m->name))
241 continue;
242
243 if (!sdp_media_proto_cmp(m, proto, update_proto))
244 continue;
245
246 return m;
247 }
248
249 return NULL;
250}
251
252
253/**
254 * Align the locate/remote formats of an SDP Media line
255 *
256 * @param m SDP Media line
257 * @param offer True if SDP Offer, False if SDP Answer
258 */
259void sdp_media_align_formats(struct sdp_media *m, bool offer)
260{
261 struct sdp_format *rfmt, *lfmt;
262 struct le *rle, *lle;
263
264 if (!m || m->disabled || !sa_port(&m->raddr) || m->fmt_ignore)
265 return;
266
267 for (lle=m->lfmtl.head; lle; lle=lle->next) {
268
269 lfmt = lle->data;
270
271 lfmt->rparams = mem_deref(lfmt->rparams);
272 lfmt->sup = false;
273 }
274
275 for (rle=m->rfmtl.head; rle; rle=rle->next) {
276
277 rfmt = rle->data;
278
279 for (lle=m->lfmtl.head; lle; lle=lle->next) {
280
281 lfmt = lle->data;
282
283 if (sdp_format_cmp(lfmt, rfmt))
284 break;
285 }
286
287 if (!lle) {
288 rfmt->sup = false;
289 continue;
290 }
291
292 mem_deref(lfmt->rparams);
293 lfmt->rparams = mem_ref(rfmt->params);
294
295 lfmt->sup = true;
296 rfmt->sup = true;
297
298 if (rfmt->ref)
299 rfmt->data = mem_deref(rfmt->data);
300 else
301 rfmt->data = NULL;
302
303 if (lfmt->ref)
304 rfmt->data = mem_ref(lfmt->data);
305 else
306 rfmt->data = lfmt->data;
307
308 rfmt->ref = lfmt->ref;
309
310 if (offer) {
311 mem_deref(lfmt->id);
312 lfmt->id = mem_ref(rfmt->id);
313 lfmt->pt = atoi(lfmt->id ? lfmt->id : "");
314
315 list_unlink(&lfmt->le);
316 list_append(&m->lfmtl, &lfmt->le, lfmt);
317 }
318 }
319
320 if (offer) {
321
322 for (lle=m->lfmtl.tail; lle; ) {
323
324 lfmt = lle->data;
325
326 lle = lle->prev;
327
328 if (!lfmt->sup) {
329 list_unlink(&lfmt->le);
330 list_append(&m->lfmtl, &lfmt->le, lfmt);
331 }
332 }
333 }
334}
335
336
337/**
338 * Set alternative protocols for an SDP Media line
339 *
340 * @param m SDP Media line
341 * @param protoc Number of alternative protocols
342 *
343 * @return 0 if success, otherwise errorcode
344 */
345int sdp_media_set_alt_protos(struct sdp_media *m, unsigned protoc, ...)
346{
347 const char *proto;
348 int err = 0;
349 unsigned i;
350 va_list ap;
351
352 if (!m)
353 return EINVAL;
354
355 va_start(ap, protoc);
356
357 for (i=0; i<ARRAY_SIZE(m->protov); i++) {
358
359 m->protov[i] = mem_deref(m->protov[i]);
360
361 if (i >= protoc)
362 continue;
363
364 proto = va_arg(ap, const char *);
365 if (proto)
366 err |= str_dup(&m->protov[i], proto);
367 }
368
369 va_end(ap);
370
371 return err;
372}
373
374
375/**
376 * Set SDP Media line encode handler
377 *
378 * @param m SDP Media line
379 * @param ench Encode handler
380 * @param arg Encode handler argument
381 */
382void sdp_media_set_encode_handler(struct sdp_media *m, sdp_media_enc_h *ench,
383 void *arg)
384{
385 if (!m)
386 return;
387
388 m->ench = ench;
389 m->arg = arg;
390}
391
392
393/**
394 * Set an SDP Media line to ignore formats
395 *
396 * @param m SDP Media line
397 * @param fmt_ignore True for ignore formats, otherwise false
398 */
399void sdp_media_set_fmt_ignore(struct sdp_media *m, bool fmt_ignore)
400{
401 if (!m)
402 return;
403
404 m->fmt_ignore = fmt_ignore;
405}
406
407
408/**
409 * Set an SDP Media line to enabled/disabled
410 *
411 * @param m SDP Media line
412 * @param disabled True for disabled, False for enabled
413 */
414void sdp_media_set_disabled(struct sdp_media *m, bool disabled)
415{
416 if (!m)
417 return;
418
419 m->disabled = disabled;
420}
421
422
423/**
424 * Set the local port number of an SDP Media line
425 *
426 * @param m SDP Media line
427 * @param port Port number
428 */
429void sdp_media_set_lport(struct sdp_media *m, uint16_t port)
430{
431 if (!m)
432 return;
433
434 sa_set_port(&m->laddr, port);
435}
436
437
438/**
439 * Set the local network address of an SDP media line
440 *
441 * @param m SDP Media line
442 * @param laddr Local network address
443 */
444void sdp_media_set_laddr(struct sdp_media *m, const struct sa *laddr)
445{
446 if (!m || !laddr)
447 return;
448
449 m->laddr = *laddr;
450}
451
452
453/**
454 * Set a local bandwidth of an SDP Media line
455 *
456 * @param m SDP Media line
457 * @param type Bandwidth type
458 * @param bw Bandwidth value
459 */
460void sdp_media_set_lbandwidth(struct sdp_media *m, enum sdp_bandwidth type,
461 int32_t bw)
462{
463 if (!m || type >= SDP_BANDWIDTH_MAX)
464 return;
465
466 m->lbwv[type] = bw;
467}
468
469
470/**
471 * Set the local RTCP port number of an SDP Media line
472 *
473 * @param m SDP Media line
474 * @param port RTCP Port number
475 */
476void sdp_media_set_lport_rtcp(struct sdp_media *m, uint16_t port)
477{
478 if (!m)
479 return;
480
481 sa_set_port(&m->laddr_rtcp, port);
482}
483
484
485/**
486 * Set the local RTCP network address of an SDP media line
487 *
488 * @param m SDP Media line
489 * @param laddr Local RTCP network address
490 */
491void sdp_media_set_laddr_rtcp(struct sdp_media *m, const struct sa *laddr)
492{
493 if (!m || !laddr)
494 return;
495
496 m->laddr_rtcp = *laddr;
497}
498
499
500/**
501 * Set the local direction flag of an SDP Media line
502 *
503 * @param m SDP Media line
504 * @param dir Media direction flag
505 */
506void sdp_media_set_ldir(struct sdp_media *m, enum sdp_dir dir)
507{
508 if (!m)
509 return;
510
511 m->ldir = dir;
512}
513
514
515/**
516 * Set a local attribute of an SDP Media line
517 *
518 * @param m SDP Media line
519 * @param replace True to replace attribute, False to append
520 * @param name Attribute name
521 * @param value Formatted attribute value
522 *
523 * @return 0 if success, otherwise errorcode
524 */
525int sdp_media_set_lattr(struct sdp_media *m, bool replace,
526 const char *name, const char *value, ...)
527{
528 va_list ap;
529 int err;
530
531 if (!m || !name)
532 return EINVAL;
533
534 if (replace)
535 sdp_attr_del(&m->lattrl, name);
536
537 va_start(ap, value);
538 err = sdp_attr_addv(&m->lattrl, name, value, ap);
539 va_end(ap);
540
541 return err;
542}
543
544
545/**
546 * Delete a local attribute of an SDP Media line
547 *
548 * @param m SDP Media line
549 * @param name Attribute name
550 */
551void sdp_media_del_lattr(struct sdp_media *m, const char *name)
552{
553 if (!m || !name)
554 return;
555
556 sdp_attr_del(&m->lattrl, name);
557}
558
559
560const char *sdp_media_proto(const struct sdp_media *m)
561{
562 return m ? m->proto : NULL;
563}
564
565
566/**
567 * Get the remote port number of an SDP Media line
568 *
569 * @param m SDP Media line
570 *
571 * @return Remote port number
572 */
573uint16_t sdp_media_rport(const struct sdp_media *m)
574{
575 return m ? sa_port(&m->raddr) : 0;
576}
577
578
579/**
580 * Get the remote network address of an SDP Media line
581 *
582 * @param m SDP Media line
583 *
584 * @return Remote network address
585 */
586const struct sa *sdp_media_raddr(const struct sdp_media *m)
587{
588 return m ? &m->raddr : NULL;
589}
590
591
592/**
593 * Get the local network address of an SDP Media line
594 *
595 * @param m SDP Media line
596 *
597 * @return Local network address
598 */
599const struct sa *sdp_media_laddr(const struct sdp_media *m)
600{
601 return m ? &m->laddr : NULL;
602}
603
604
605/**
606 * Get the remote RTCP network address of an SDP Media line
607 *
608 * @param m SDP Media line
609 * @param raddr On return, contains remote RTCP network address
610 */
611void sdp_media_raddr_rtcp(const struct sdp_media *m, struct sa *raddr)
612{
613 if (!m || !raddr)
614 return;
615
616 if (sa_isset(&m->raddr_rtcp, SA_ALL)) {
617 *raddr = m->raddr_rtcp;
618 }
619 else if (sa_isset(&m->raddr_rtcp, SA_PORT)) {
620 *raddr = m->raddr;
621 sa_set_port(raddr, sa_port(&m->raddr_rtcp));
622 }
623 else {
624 uint16_t port = sa_port(&m->raddr);
625
626 *raddr = m->raddr;
627 sa_set_port(raddr, port ? port + 1 : 0);
628 }
629}
630
631
632/**
633 * Get a remote bandwidth of an SDP Media line
634 *
635 * @param m SDP Media line
636 * @param type Bandwidth type
637 *
638 * @return Remote bandwidth value
639 */
640int32_t sdp_media_rbandwidth(const struct sdp_media *m,
641 enum sdp_bandwidth type)
642{
643 if (!m || type >= SDP_BANDWIDTH_MAX)
644 return 0;
645
646 return m->rbwv[type];
647}
648
649
650/**
651 * Get the local media direction of an SDP Media line
652 *
653 * @param m SDP Media line
654 *
655 * @return Local media direction
656 */
657enum sdp_dir sdp_media_ldir(const struct sdp_media *m)
658{
659 return m ? m->ldir : SDP_INACTIVE;
660}
661
662
663/**
664 * Get the remote media direction of an SDP Media line
665 *
666 * @param m SDP Media line
667 *
668 * @return Remote media direction
669 */
670enum sdp_dir sdp_media_rdir(const struct sdp_media *m)
671{
672 return m ? m->rdir : SDP_INACTIVE;
673}
674
675
676/**
677 * Get the combined media direction of an SDP Media line
678 *
679 * @param m SDP Media line
680 *
681 * @return Combined media direction
682 */
683enum sdp_dir sdp_media_dir(const struct sdp_media *m)
684{
685 return m ? (enum sdp_dir)(m->ldir & m->rdir) : SDP_INACTIVE;
686}
687
688
689/**
690 * Find a local SDP format from a payload type
691 *
692 * @param m SDP Media line
693 * @param pt Payload type
694 *
695 * @return Local SDP format if found, NULL if not found
696 */
697const struct sdp_format *sdp_media_lformat(const struct sdp_media *m, int pt)
698{
699 struct le *le;
700
701 if (!m)
702 return NULL;
703
704 for (le=m->lfmtl.head; le; le=le->next) {
705
706 const struct sdp_format *fmt = le->data;
707
708 if (pt == fmt->pt)
709 return fmt;
710 }
711
712 return NULL;
713}
714
715
716/**
717 * Find a remote SDP format from a format name
718 *
719 * @param m SDP Media line
720 * @param name Format name
721 *
722 * @return Remote SDP format if found, NULL if not found
723 */
724const struct sdp_format *sdp_media_rformat(const struct sdp_media *m,
725 const char *name)
726{
727 struct le *le;
728
729 if (!m || !sa_port(&m->raddr))
730 return NULL;
731
732 for (le=m->rfmtl.head; le; le=le->next) {
733
734 const struct sdp_format *fmt = le->data;
735
736 if (!fmt->sup)
737 continue;
738
739 if (name && str_casecmp(name, fmt->name))
740 continue;
741
742 return fmt;
743 }
744
745 return NULL;
746}
747
748
749/**
750 * Find an SDP Format of an SDP Media line
751 *
752 * @param m SDP Media line
753 * @param local True if local media, False if remote
754 * @param id SDP format id
755 * @param pt Payload type
756 * @param name Format name
757 * @param srate Sampling rate
758 * @param ch Number of channels
759 *
760 * @return SDP Format if found, NULL if not found
761 */
762struct sdp_format *sdp_media_format(const struct sdp_media *m,
763 bool local, const char *id,
764 int pt, const char *name,
765 int32_t srate, int8_t ch)
766{
767 return sdp_media_format_apply(m, local, id, pt, name, srate, ch,
768 NULL, NULL);
769}
770
771
772/**
773 * Apply a function handler to all matching SDP formats
774 *
775 * @param m SDP Media line
776 * @param local True if local media, False if remote
777 * @param id SDP format id
778 * @param pt Payload type
779 * @param name Format name
780 * @param srate Sampling rate
781 * @param ch Number of channels
782 * @param fmth SDP Format handler
783 * @param arg Handler argument
784 *
785 * @return SDP Format if found, NULL if not found
786 */
787struct sdp_format *sdp_media_format_apply(const struct sdp_media *m,
788 bool local, const char *id,
789 int pt, const char *name,
790 int32_t srate, int8_t ch,
791 sdp_format_h *fmth, void *arg)
792{
793 struct le *le;
794
795 if (!m)
796 return NULL;
797
798 le = local ? m->lfmtl.head : m->rfmtl.head;
799
800 while (le) {
801
802 struct sdp_format *fmt = le->data;
803
804 le = le->next;
805
806 if (id && (!fmt->id || strcmp(id, fmt->id)))
807 continue;
808
809 if (pt >= 0 && pt != fmt->pt)
810 continue;
811
812 if (name && str_casecmp(name, fmt->name))
813 continue;
814
815 if (srate >= 0 && (uint32_t)srate != fmt->srate)
816 continue;
817
818 if (ch >= 0 && (uint8_t)ch != fmt->ch)
819 continue;
820
821 if (!fmth || fmth(fmt, arg))
822 return fmt;
823 }
824
825 return NULL;
826}
827
828
829/**
830 * Get the list of SDP Formats
831 *
832 * @param m SDP Media line
833 * @param local True if local, False if remote
834 *
835 * @return List of SDP Formats
836 */
837const struct list *sdp_media_format_lst(const struct sdp_media *m, bool local)
838{
839 if (!m)
840 return NULL;
841
842 return local ? &m->lfmtl : &m->rfmtl;
843}
844
845
846/**
847 * Get a remote attribute from an SDP Media line
848 *
849 * @param m SDP Media line
850 * @param name Attribute name
851 *
852 * @return Attribute value, NULL if not found
853 */
854const char *sdp_media_rattr(const struct sdp_media *m, const char *name)
855{
856 if (!m || !name)
857 return NULL;
858
859 return sdp_attr_apply(&m->rattrl, name, NULL, NULL);
860}
861
862
863/**
864 * Get a remote attribute from an SDP Media line or the SDP session
865 *
866 * @param m SDP Media line
867 * @param sess SDP Session
868 * @param name Attribute name
869 *
870 * @return Attribute value, NULL if not found
871 */
872const char *sdp_media_session_rattr(const struct sdp_media *m,
873 const struct sdp_session *sess,
874 const char *name)
875{
876 const char *val;
877
878 val = sdp_media_rattr(m, name);
879 if (!val)
880 val = sdp_session_rattr(sess, name);
881
882 return val;
883}
884
885
886/**
887 * Apply a function handler to all matching remote attributes
888 *
889 * @param m SDP Media line
890 * @param name Attribute name
891 * @param attrh Attribute handler
892 * @param arg Handler argument
893 *
894 * @return Attribute value if match
895 */
896const char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name,
897 sdp_attr_h *attrh, void *arg)
898{
899 if (!m)
900 return NULL;
901
902 return sdp_attr_apply(&m->rattrl, name, attrh, arg);
903}
904
905
906/**
907 * Get the name of an SDP Media line
908 *
909 * @param m SDP Media line
910 *
911 * @return SDP Media line name
912 */
913const char *sdp_media_name(const struct sdp_media *m)
914{
915 return m ? m->name : NULL;
916}
917
918
919/**
920 * Print SDP Media line debug information
921 *
922 * @param pf Print function for output
923 * @param m SDP Media line
924 *
925 * @return 0 if success, otherwise errorcode
926 */
927int sdp_media_debug(struct re_printf *pf, const struct sdp_media *m)
928{
929 struct le *le;
930 int err;
931
932 if (!m)
933 return 0;
934
935 err = re_hprintf(pf, "%s %s\n", m->name, m->proto);
936
937 err |= re_hprintf(pf, " local formats:\n");
938
939 for (le=m->lfmtl.head; le; le=le->next)
940 err |= re_hprintf(pf, " %H\n", sdp_format_debug, le->data);
941
942 err |= re_hprintf(pf, " remote formats:\n");
943
944 for (le=m->rfmtl.head; le; le=le->next)
945 err |= re_hprintf(pf, " %H\n", sdp_format_debug, le->data);
946
947 err |= re_hprintf(pf, " local attributes:\n");
948
949 for (le=m->lattrl.head; le; le=le->next)
950 err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data);
951
952 err |= re_hprintf(pf, " remote attributes:\n");
953
954 for (le=m->rattrl.head; le; le=le->next)
955 err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data);
956
957 return err;
958}