re/src/sdp/media.c
2014-10-08 11:21:31 +00:00

958 lines
17 KiB
C

/**
* @file sdp/media.c SDP Media
*
* Copyright (C) 2010 Creytiv.com
*/
#include <stdlib.h>
#include <string.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_list.h>
#include <re_sa.h>
#include <re_sdp.h>
#include "sdp.h"
static void destructor(void *arg)
{
struct sdp_media *m = arg;
unsigned i;
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;
}
for (i=0; i<ARRAY_SIZE(m->protov); i++)
mem_deref(m->protov[i]);
list_unlink(&m->le);
mem_deref(m->name);
mem_deref(m->proto);
mem_deref(m->uproto);
}
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; i<SDP_BANDWIDTH_MAX; i++) {
m->lbwv[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; i<SDP_BANDWIDTH_MAX; i++)
m->rbwv[i] = -1;
}
/**
* Compare media line protocols
*
* @param m SDP Media line
* @param proto Transport protocol
* @param update Update media protocol if match is found in alternate set
*
* @return True if matching, False if not
*/
bool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto,
bool update)
{
unsigned i;
if (!m || !proto)
return false;
if (!pl_strcmp(proto, m->proto))
return true;
for (i=0; i<ARRAY_SIZE(m->protov); i++) {
if (!m->protov[i] || pl_strcmp(proto, m->protov[i]))
continue;
if (update) {
mem_deref(m->proto);
m->proto = mem_ref(m->protov[i]);
}
return true;
}
return false;
}
/**
* Find an SDP Media line from name and transport protocol
*
* @param sess SDP Session
* @param name Media name
* @param proto Transport protocol
* @param update_proto Update media 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,
bool update_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 (!sdp_media_proto_cmp(m, proto, update_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 alternative protocols for an SDP Media line
*
* @param m SDP Media line
* @param protoc Number of alternative protocols
*
* @return 0 if success, otherwise errorcode
*/
int sdp_media_set_alt_protos(struct sdp_media *m, unsigned protoc, ...)
{
const char *proto;
int err = 0;
unsigned i;
va_list ap;
if (!m)
return EINVAL;
va_start(ap, protoc);
for (i=0; i<ARRAY_SIZE(m->protov); i++) {
m->protov[i] = mem_deref(m->protov[i]);
if (i >= protoc)
continue;
proto = va_arg(ap, const char *);
if (proto)
err |= str_dup(&m->protov[i], proto);
}
va_end(ap);
return err;
}
/**
* 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);
}
const char *sdp_media_proto(const struct sdp_media *m)
{
return m ? m->proto : NULL;
}
/**
* 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 local network address of an SDP Media line
*
* @param m SDP Media line
*
* @return Local network address
*/
const struct sa *sdp_media_laddr(const struct sdp_media *m)
{
return m ? &m->laddr : 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);
}
/**
* Get a remote attribute from an SDP Media line or the SDP session
*
* @param m SDP Media line
* @param sess SDP Session
* @param name Attribute name
*
* @return Attribute value, NULL if not found
*/
const char *sdp_media_session_rattr(const struct sdp_media *m,
const struct sdp_session *sess,
const char *name)
{
const char *val;
val = sdp_media_rattr(m, name);
if (!val)
val = sdp_session_rattr(sess, name);
return val;
}
/**
* 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;
}