/** * @file sdp/media.c SDP Media * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "sdp.h" static void destructor(void *arg) { struct sdp_media *m = arg; list_flush(&m->lfmtl); list_flush(&m->rfmtl); list_flush(&m->rattrl); list_flush(&m->lattrl); if (m->le.list) { m->disabled = true; m->ench = NULL; mem_ref(m); return; } list_unlink(&m->le); mem_deref(m->name); mem_deref(m->proto); } static int media_alloc(struct sdp_media **mp, struct list *list) { struct sdp_media *m; int i; m = mem_zalloc(sizeof(*m), destructor); if (!m) return ENOMEM; list_append(list, &m->le, m); m->ldir = SDP_SENDRECV; m->rdir = SDP_SENDRECV; m->dynpt = RTP_DYNPT_START; sa_init(&m->laddr, AF_INET); sa_init(&m->raddr, AF_INET); sa_init(&m->laddr_rtcp, AF_INET); sa_init(&m->raddr_rtcp, AF_INET); for (i=0; ilbwv[i] = -1; m->rbwv[i] = -1; } *mp = m; return 0; } /** * Add a media line to an SDP Session * * @param mp Pointer to allocated SDP Media line object * @param sess SDP Session * @param name Media name * @param port Port number * @param proto Transport protocol * * @return 0 if success, otherwise errorcode */ int sdp_media_add(struct sdp_media **mp, struct sdp_session *sess, const char *name, uint16_t port, const char *proto) { struct sdp_media *m; int err; if (!sess || !name || !proto) return EINVAL; err = media_alloc(&m, &sess->lmedial); if (err) return err; err = str_dup(&m->name, name); err |= str_dup(&m->proto, proto); if (err) goto out; sa_set_port(&m->laddr, port); out: if (err) mem_deref(m); else if (mp) *mp = m; return err; } /** * Add a remote SDP media line to an SDP Session * * @param mp Pointer to allocated SDP Media line object * @param sess SDP Session * @param name Media name * @param proto Transport protocol * * @return 0 if success, otherwise errorcode */ int sdp_media_radd(struct sdp_media **mp, struct sdp_session *sess, const struct pl *name, const struct pl *proto) { struct sdp_media *m; int err; if (!mp || !sess || !name || !proto) return EINVAL; err = media_alloc(&m, &sess->medial); if (err) return err; m->disabled = true; err = pl_strdup(&m->name, name); err |= pl_strdup(&m->proto, proto); if (err) mem_deref(m); else *mp = m; return err; } /** * Reset the remote part of an SDP Media line * * @param m SDP Media line */ void sdp_media_rreset(struct sdp_media *m) { int i; if (!m) return; sa_init(&m->raddr, AF_INET); sa_init(&m->raddr_rtcp, AF_INET); list_flush(&m->rfmtl); list_flush(&m->rattrl); m->rdir = SDP_SENDRECV; for (i=0; irbwv[i] = -1; } /** * Find an SDP Media line from name and transport protocol * * @param sess SDP Session * @param name Media name * @param proto Transport protocol * * @return Matching media line if found, NULL if not found */ struct sdp_media *sdp_media_find(const struct sdp_session *sess, const struct pl *name, const struct pl *proto) { struct le *le; if (!sess || !name || !proto) return NULL; for (le=sess->lmedial.head; le; le=le->next) { struct sdp_media *m = le->data; if (pl_strcmp(name, m->name)) continue; if (pl_strcmp(proto, m->proto)) continue; return m; } return NULL; } /** * Align the locate/remote formats of an SDP Media line * * @param m SDP Media line * @param offer True if SDP Offer, False if SDP Answer */ void sdp_media_align_formats(struct sdp_media *m, bool offer) { struct sdp_format *rfmt, *lfmt; struct le *rle, *lle; if (!m || m->disabled || !sa_port(&m->raddr) || m->fmt_ignore) return; for (lle=m->lfmtl.head; lle; lle=lle->next) { lfmt = lle->data; lfmt->rparams = mem_deref(lfmt->rparams); lfmt->sup = false; } for (rle=m->rfmtl.head; rle; rle=rle->next) { rfmt = rle->data; for (lle=m->lfmtl.head; lle; lle=lle->next) { lfmt = lle->data; if (sdp_format_cmp(lfmt, rfmt)) break; } if (!lle) { rfmt->sup = false; continue; } mem_deref(lfmt->rparams); lfmt->rparams = mem_ref(rfmt->params); lfmt->sup = true; rfmt->sup = true; if (rfmt->ref) rfmt->data = mem_deref(rfmt->data); else rfmt->data = NULL; if (lfmt->ref) rfmt->data = mem_ref(lfmt->data); else rfmt->data = lfmt->data; rfmt->ref = lfmt->ref; if (offer) { mem_deref(lfmt->id); lfmt->id = mem_ref(rfmt->id); lfmt->pt = atoi(lfmt->id ? lfmt->id : ""); list_unlink(&lfmt->le); list_append(&m->lfmtl, &lfmt->le, lfmt); } } if (offer) { for (lle=m->lfmtl.tail; lle; ) { lfmt = lle->data; lle = lle->prev; if (!lfmt->sup) { list_unlink(&lfmt->le); list_append(&m->lfmtl, &lfmt->le, lfmt); } } } } /** * Set SDP Media line encode handler * * @param m SDP Media line * @param ench Encode handler * @param arg Encode handler argument */ void sdp_media_set_encode_handler(struct sdp_media *m, sdp_media_enc_h *ench, void *arg) { if (!m) return; m->ench = ench; m->arg = arg; } /** * Set an SDP Media line to ignore formats * * @param m SDP Media line * @param fmt_ignore True for ignore formats, otherwise false */ void sdp_media_set_fmt_ignore(struct sdp_media *m, bool fmt_ignore) { if (!m) return; m->fmt_ignore = fmt_ignore; } /** * Set an SDP Media line to enabled/disabled * * @param m SDP Media line * @param disabled True for disabled, False for enabled */ void sdp_media_set_disabled(struct sdp_media *m, bool disabled) { if (!m) return; m->disabled = disabled; } /** * Set the local port number of an SDP Media line * * @param m SDP Media line * @param port Port number */ void sdp_media_set_lport(struct sdp_media *m, uint16_t port) { if (!m) return; sa_set_port(&m->laddr, port); } /** * Set the local network address of an SDP media line * * @param m SDP Media line * @param laddr Local network address */ void sdp_media_set_laddr(struct sdp_media *m, const struct sa *laddr) { if (!m || !laddr) return; m->laddr = *laddr; } /** * Set a local bandwidth of an SDP Media line * * @param m SDP Media line * @param type Bandwidth type * @param bw Bandwidth value */ void sdp_media_set_lbandwidth(struct sdp_media *m, enum sdp_bandwidth type, int32_t bw) { if (!m || type >= SDP_BANDWIDTH_MAX) return; m->lbwv[type] = bw; } /** * Set the local RTCP port number of an SDP Media line * * @param m SDP Media line * @param port RTCP Port number */ void sdp_media_set_lport_rtcp(struct sdp_media *m, uint16_t port) { if (!m) return; sa_set_port(&m->laddr_rtcp, port); } /** * Set the local RTCP network address of an SDP media line * * @param m SDP Media line * @param laddr Local RTCP network address */ void sdp_media_set_laddr_rtcp(struct sdp_media *m, const struct sa *laddr) { if (!m || !laddr) return; m->laddr_rtcp = *laddr; } /** * Set the local direction flag of an SDP Media line * * @param m SDP Media line * @param dir Media direction flag */ void sdp_media_set_ldir(struct sdp_media *m, enum sdp_dir dir) { if (!m) return; m->ldir = dir; } /** * Set a local attribute of an SDP Media line * * @param m SDP Media line * @param replace True to replace attribute, False to append * @param name Attribute name * @param value Formatted attribute value * * @return 0 if success, otherwise errorcode */ int sdp_media_set_lattr(struct sdp_media *m, bool replace, const char *name, const char *value, ...) { va_list ap; int err; if (!m || !name) return EINVAL; if (replace) sdp_attr_del(&m->lattrl, name); va_start(ap, value); err = sdp_attr_addv(&m->lattrl, name, value, ap); va_end(ap); return err; } /** * Delete a local attribute of an SDP Media line * * @param m SDP Media line * @param name Attribute name */ void sdp_media_del_lattr(struct sdp_media *m, const char *name) { if (!m || !name) return; sdp_attr_del(&m->lattrl, name); } /** * Get the remote port number of an SDP Media line * * @param m SDP Media line * * @return Remote port number */ uint16_t sdp_media_rport(const struct sdp_media *m) { return m ? sa_port(&m->raddr) : 0; } /** * Get the remote network address of an SDP Media line * * @param m SDP Media line * * @return Remote network address */ const struct sa *sdp_media_raddr(const struct sdp_media *m) { return m ? &m->raddr : NULL; } /** * Get the remote RTCP network address of an SDP Media line * * @param m SDP Media line * @param raddr On return, contains remote RTCP network address */ void sdp_media_raddr_rtcp(const struct sdp_media *m, struct sa *raddr) { if (!m || !raddr) return; if (sa_isset(&m->raddr_rtcp, SA_ALL)) { *raddr = m->raddr_rtcp; } else if (sa_isset(&m->raddr_rtcp, SA_PORT)) { *raddr = m->raddr; sa_set_port(raddr, sa_port(&m->raddr_rtcp)); } else { uint16_t port = sa_port(&m->raddr); *raddr = m->raddr; sa_set_port(raddr, port ? port + 1 : 0); } } /** * Get a remote bandwidth of an SDP Media line * * @param m SDP Media line * @param type Bandwidth type * * @return Remote bandwidth value */ int32_t sdp_media_rbandwidth(const struct sdp_media *m, enum sdp_bandwidth type) { if (!m || type >= SDP_BANDWIDTH_MAX) return 0; return m->rbwv[type]; } /** * Get the local media direction of an SDP Media line * * @param m SDP Media line * * @return Local media direction */ enum sdp_dir sdp_media_ldir(const struct sdp_media *m) { return m ? m->ldir : SDP_INACTIVE; } /** * Get the remote media direction of an SDP Media line * * @param m SDP Media line * * @return Remote media direction */ enum sdp_dir sdp_media_rdir(const struct sdp_media *m) { return m ? m->rdir : SDP_INACTIVE; } /** * Get the combined media direction of an SDP Media line * * @param m SDP Media line * * @return Combined media direction */ enum sdp_dir sdp_media_dir(const struct sdp_media *m) { return m ? (enum sdp_dir)(m->ldir & m->rdir) : SDP_INACTIVE; } /** * Find a local SDP format from a payload type * * @param m SDP Media line * @param pt Payload type * * @return Local SDP format if found, NULL if not found */ const struct sdp_format *sdp_media_lformat(const struct sdp_media *m, int pt) { struct le *le; if (!m) return NULL; for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (pt == fmt->pt) return fmt; } return NULL; } /** * Find a remote SDP format from a format name * * @param m SDP Media line * @param name Format name * * @return Remote SDP format if found, NULL if not found */ const struct sdp_format *sdp_media_rformat(const struct sdp_media *m, const char *name) { struct le *le; if (!m || !sa_port(&m->raddr)) return NULL; for (le=m->rfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (!fmt->sup) continue; if (name && str_casecmp(name, fmt->name)) continue; return fmt; } return NULL; } /** * Find an SDP Format of an SDP Media line * * @param m SDP Media line * @param local True if local media, False if remote * @param id SDP format id * @param pt Payload type * @param name Format name * @param srate Sampling rate * @param ch Number of channels * * @return SDP Format if found, NULL if not found */ struct sdp_format *sdp_media_format(const struct sdp_media *m, bool local, const char *id, int pt, const char *name, int32_t srate, int8_t ch) { return sdp_media_format_apply(m, local, id, pt, name, srate, ch, NULL, NULL); } /** * Apply a function handler to all matching SDP formats * * @param m SDP Media line * @param local True if local media, False if remote * @param id SDP format id * @param pt Payload type * @param name Format name * @param srate Sampling rate * @param ch Number of channels * @param fmth SDP Format handler * @param arg Handler argument * * @return SDP Format if found, NULL if not found */ struct sdp_format *sdp_media_format_apply(const struct sdp_media *m, bool local, const char *id, int pt, const char *name, int32_t srate, int8_t ch, sdp_format_h *fmth, void *arg) { struct le *le; if (!m) return NULL; le = local ? m->lfmtl.head : m->rfmtl.head; while (le) { struct sdp_format *fmt = le->data; le = le->next; if (id && (!fmt->id || strcmp(id, fmt->id))) continue; if (pt >= 0 && pt != fmt->pt) continue; if (name && str_casecmp(name, fmt->name)) continue; if (srate >= 0 && (uint32_t)srate != fmt->srate) continue; if (ch >= 0 && (uint8_t)ch != fmt->ch) continue; if (!fmth || fmth(fmt, arg)) return fmt; } return NULL; } /** * Get the list of SDP Formats * * @param m SDP Media line * @param local True if local, False if remote * * @return List of SDP Formats */ const struct list *sdp_media_format_lst(const struct sdp_media *m, bool local) { if (!m) return NULL; return local ? &m->lfmtl : &m->rfmtl; } /** * Get a remote attribute from an SDP Media line * * @param m SDP Media line * @param name Attribute name * * @return Attribute value, NULL if not found */ const char *sdp_media_rattr(const struct sdp_media *m, const char *name) { if (!m || !name) return NULL; return sdp_attr_apply(&m->rattrl, name, NULL, NULL); } /** * Apply a function handler to all matching remote attributes * * @param m SDP Media line * @param name Attribute name * @param attrh Attribute handler * @param arg Handler argument * * @return Attribute value if match */ const char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name, sdp_attr_h *attrh, void *arg) { if (!m) return NULL; return sdp_attr_apply(&m->rattrl, name, attrh, arg); } /** * Get the name of an SDP Media line * * @param m SDP Media line * * @return SDP Media line name */ const char *sdp_media_name(const struct sdp_media *m) { return m ? m->name : NULL; } /** * Print SDP Media line debug information * * @param pf Print function for output * @param m SDP Media line * * @return 0 if success, otherwise errorcode */ int sdp_media_debug(struct re_printf *pf, const struct sdp_media *m) { struct le *le; int err; if (!m) return 0; err = re_hprintf(pf, "%s %s\n", m->name, m->proto); err |= re_hprintf(pf, " local formats:\n"); for (le=m->lfmtl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_format_debug, le->data); err |= re_hprintf(pf, " remote formats:\n"); for (le=m->rfmtl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_format_debug, le->data); err |= re_hprintf(pf, " local attributes:\n"); for (le=m->lattrl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data); err |= re_hprintf(pf, " remote attributes:\n"); for (le=m->rattrl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data); return err; }