summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia/endpoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia/endpoint.c')
-rw-r--r--pjmedia/src/pjmedia/endpoint.c371
1 files changed, 294 insertions, 77 deletions
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index 112a0981..75831f1f 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -20,6 +20,7 @@
#include <pjmedia/endpoint.h>
#include <pjmedia/errno.h>
#include <pjmedia/sdp.h>
+#include <pjmedia/vid_codec.h>
#include <pjmedia-audiodev/audiodev.h>
#include <pj/assert.h>
#include <pj/ioqueue.h>
@@ -312,87 +313,37 @@ PJ_DEF(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt,
return pj_pool_create(endpt->pf, name, initial, increment, NULL);
}
-/**
- * Create a SDP session description that describes the endpoint
- * capability.
- */
-PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
- pj_pool_t *pool,
- unsigned stream_cnt,
- const pjmedia_sock_info sock_info[],
- pjmedia_sdp_session **p_sdp )
+/* Common initialization for both audio and video SDP media line */
+static pj_status_t init_sdp_media(pjmedia_sdp_media *m,
+ pj_pool_t *pool,
+ const pj_str_t *media_type,
+ const pjmedia_sock_info *sock_info)
{
- pj_time_val tv;
- unsigned i;
- const pj_sockaddr *addr0;
- pjmedia_sdp_session *sdp;
- pjmedia_sdp_media *m;
+ char tmp_addr[PJ_INET6_ADDRSTRLEN];
pjmedia_sdp_attr *attr;
+ const pj_sockaddr *addr;
- /* Sanity check arguments */
- PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL);
-
- /* Check that there are not too many codecs */
- PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT,
- PJ_ETOOMANY);
-
- /* Create and initialize basic SDP session */
- sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
-
- addr0 = &sock_info[0].rtp_addr_name;
-
- pj_gettimeofday(&tv);
- sdp->origin.user = pj_str("-");
- sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
- sdp->origin.net_type = STR_IN;
-
- if (addr0->addr.sa_family == pj_AF_INET()) {
- sdp->origin.addr_type = STR_IP4;
- pj_strdup2(pool, &sdp->origin.addr,
- pj_inet_ntoa(addr0->ipv4.sin_addr));
- } else if (addr0->addr.sa_family == pj_AF_INET6()) {
- char tmp_addr[PJ_INET6_ADDRSTRLEN];
-
- sdp->origin.addr_type = STR_IP6;
- pj_strdup2(pool, &sdp->origin.addr,
- pj_sockaddr_print(addr0, tmp_addr, sizeof(tmp_addr), 0));
-
- } else {
- pj_assert(!"Invalid address family");
- return PJ_EAFNOTSUP;
- }
-
- sdp->name = STR_SDP_NAME;
-
- /* Since we only support one media stream at present, put the
- * SDP connection line in the session level.
- */
- sdp->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
- sdp->conn->net_type = sdp->origin.net_type;
- sdp->conn->addr_type = sdp->origin.addr_type;
- sdp->conn->addr = sdp->origin.addr;
+ pj_strdup(pool, &m->desc.media, media_type);
+ addr = &sock_info->rtp_addr_name;
- /* SDP time and attributes. */
- sdp->time.start = sdp->time.stop = 0;
- sdp->attr_count = 0;
+ /* Validate address family */
+ PJ_ASSERT_RETURN(addr->addr.sa_family == pj_AF_INET() ||
+ addr->addr.sa_family == pj_AF_INET6(),
+ PJ_EAFNOTSUP);
- /* Create media stream 0: */
+ /* SDP connection line */
+ m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+ m->conn->net_type = STR_IN;
+ m->conn->addr_type = (addr->addr.sa_family==pj_AF_INET())? STR_IP4:STR_IP6;
+ pj_sockaddr_print(addr, tmp_addr, sizeof(tmp_addr), 0);
+ pj_strdup2(pool, &m->conn->addr, tmp_addr);
- sdp->media_count = 1;
- m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
- sdp->media[0] = m;
-
- /* Standard media info: */
- pj_strdup(pool, &m->desc.media, &STR_AUDIO);
- m->desc.port = pj_sockaddr_get_port(addr0);
+ /* Port and transport in media description */
+ m->desc.port = pj_sockaddr_get_port(addr);
m->desc.port_count = 1;
pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP);
- /* Init media line and attribute list. */
- m->desc.fmt_count = 0;
- m->attr_count = 0;
-
/* Add "rtcp" attribute */
#if defined(PJMEDIA_HAS_RTCP_IN_SDP) && PJMEDIA_HAS_RTCP_IN_SDP!=0
if (sock_info->rtcp_addr_name.addr.sa_family != 0) {
@@ -402,13 +353,45 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
#endif
+ /* Add sendrecv attribute. */
+ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+ attr->name = STR_SENDRECV;
+ m->attr[m->attr_count++] = attr;
+
+ return PJ_SUCCESS;
+}
+
+/* Create m=audio SDP media line */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_sock_info *si,
+ unsigned options,
+ pjmedia_sdp_media **p_m)
+{
+ const pj_str_t STR_AUDIO = { "audio", 5 };
+ pjmedia_sdp_media *m;
+ pjmedia_sdp_attr *attr;
+ unsigned i;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(options);
+
+ /* Check that there are not too many codecs */
+ PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT,
+ PJ_ETOOMANY);
+
+ /* Create and init basic SDP media */
+ m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
+ status = init_sdp_media(m, pool, &STR_AUDIO, si);
+ if (status != PJ_SUCCESS)
+ return status;
+
/* Add format, rtpmap, and fmtp (when applicable) for each codec */
for (i=0; i<endpt->codec_mgr.codec_cnt; ++i) {
pjmedia_codec_info *codec_info;
pjmedia_sdp_rtpmap rtpmap;
char tmp_param[3];
- pjmedia_sdp_attr *attr;
pjmedia_codec_param codec_param;
pj_str_t *fmt;
@@ -512,11 +495,6 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
}
- /* Add sendrecv attribute. */
- attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
- attr->name = STR_SENDRECV;
- m->attr[m->attr_count++] = attr;
-
#if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
/*
@@ -541,11 +519,250 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
#endif
+ *p_m = m;
+ return PJ_SUCCESS;
+}
+
+/* Create m=video SDP media line */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_sock_info *si,
+ unsigned options,
+ pjmedia_sdp_media **p_m)
+{
+ const pj_str_t STR_VIDEO = { "video", 5 };
+ pjmedia_sdp_media *m;
+ pjmedia_vid_codec_info codec_info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
+ unsigned codec_prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
+ pjmedia_sdp_attr *attr;
+ unsigned cnt, i;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(options);
+
+ /* Make sure video codec manager is instantiated */
+ if (!pjmedia_vid_codec_mgr_instance())
+ pjmedia_vid_codec_mgr_create(endpt->pool, NULL);
+
+ /* Create and init basic SDP media */
+ m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
+ status = init_sdp_media(m, pool, &STR_VIDEO, si);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ cnt = PJ_ARRAY_SIZE(codec_info);
+ status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt,
+ codec_info, codec_prio);
+
+ /* Check that there are not too many codecs */
+ PJ_ASSERT_RETURN(0 <= PJMEDIA_MAX_SDP_FMT,
+ PJ_ETOOMANY);
+
+ /* Add format, rtpmap, and fmtp (when applicable) for each codec */
+ for (i=0; i<cnt; ++i) {
+ pjmedia_sdp_rtpmap rtpmap;
+ pjmedia_vid_codec_param codec_param;
+ pj_str_t *fmt;
+
+ pj_bzero(&rtpmap, sizeof(rtpmap));
+
+ if (codec_prio[i] == PJMEDIA_CODEC_PRIO_DISABLED)
+ break;
+
+ if (i > PJMEDIA_MAX_SDP_FMT) {
+ /* Too many codecs, perhaps it is better to tell application by
+ * returning appropriate status code.
+ */
+ PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY,
+ "Skipping some video codecs"));
+ break;
+ }
+
+ /* Must support RTP packetization and bidirectional */
+ if (!codec_info[i].has_rtp_pack ||
+ codec_info[i].dir != PJMEDIA_DIR_ENCODING_DECODING)
+ {
+ continue;
+ }
+
+ pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i],
+ &codec_param);
+
+ fmt = &m->desc.fmt[m->desc.fmt_count++];
+ fmt->ptr = (char*) pj_pool_alloc(pool, 8);
+ fmt->slen = pj_utoa(codec_info[i].pt, fmt->ptr);
+ rtpmap.pt = *fmt;
+
+ /* Encoding name */
+ rtpmap.enc_name = codec_info[i].encoding_name;
+
+ /* Clock rate */
+ rtpmap.clock_rate = codec_info[i].clock_rate;
+
+ if (codec_info[i].pt >= 96 || pjmedia_add_rtpmap_for_static_pt) {
+ pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
+ m->attr[m->attr_count++] = attr;
+ }
+
+ /* Add fmtp params */
+ if (codec_param.dec_fmtp.cnt > 0) {
+ enum { MAX_FMTP_STR_LEN = 160 };
+ char buf[MAX_FMTP_STR_LEN];
+ unsigned buf_len = 0, j;
+ pjmedia_codec_fmtp *dec_fmtp = &codec_param.dec_fmtp;
+
+ /* Print codec PT */
+ buf_len += pj_ansi_snprintf(buf,
+ MAX_FMTP_STR_LEN - buf_len,
+ "%d",
+ codec_info[i].pt);
+
+ for (j = 0; j < dec_fmtp->cnt; ++j) {
+ unsigned test_len = 2;
+
+ /* Check if buf still available */
+ test_len = dec_fmtp->param[j].val.slen +
+ dec_fmtp->param[j].name.slen;
+ if (test_len + buf_len >= MAX_FMTP_STR_LEN)
+ return PJ_ETOOBIG;
+
+ /* Print delimiter */
+ buf_len += pj_ansi_snprintf(&buf[buf_len],
+ MAX_FMTP_STR_LEN - buf_len,
+ (j == 0?" ":";"));
+
+ /* Print an fmtp param */
+ if (dec_fmtp->param[j].name.slen)
+ buf_len += pj_ansi_snprintf(
+ &buf[buf_len],
+ MAX_FMTP_STR_LEN - buf_len,
+ "%.*s=%.*s",
+ (int)dec_fmtp->param[j].name.slen,
+ dec_fmtp->param[j].name.ptr,
+ (int)dec_fmtp->param[j].val.slen,
+ dec_fmtp->param[j].val.ptr);
+ else
+ buf_len += pj_ansi_snprintf(&buf[buf_len],
+ MAX_FMTP_STR_LEN - buf_len,
+ "%.*s",
+ (int)dec_fmtp->param[j].val.slen,
+ dec_fmtp->param[j].val.ptr);
+ }
+
+ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+
+ attr->name = pj_str("fmtp");
+ attr->value = pj_strdup3(pool, buf);
+ m->attr[m->attr_count++] = attr;
+ }
+ }
+
+ *p_m = m;
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Create a "blank" SDP session description. The SDP will contain basic SDP
+ * fields such as origin, time, and name, but without any media lines.
+ */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_base_sdp( pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pj_str_t *sess_name,
+ const pj_sockaddr *origin,
+ pjmedia_sdp_session **p_sdp)
+{
+ pj_time_val tv;
+ pjmedia_sdp_session *sdp;
+
+ /* Sanity check arguments */
+ PJ_ASSERT_RETURN(endpt && pool && p_sdp, PJ_EINVAL);
+
+ sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
+
+ pj_gettimeofday(&tv);
+ sdp->origin.user = pj_str("-");
+ sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
+ sdp->origin.net_type = STR_IN;
+
+ if (origin->addr.sa_family == pj_AF_INET()) {
+ sdp->origin.addr_type = STR_IP4;
+ pj_strdup2(pool, &sdp->origin.addr,
+ pj_inet_ntoa(origin->ipv4.sin_addr));
+ } else if (origin->addr.sa_family == pj_AF_INET6()) {
+ char tmp_addr[PJ_INET6_ADDRSTRLEN];
+
+ sdp->origin.addr_type = STR_IP6;
+ pj_strdup2(pool, &sdp->origin.addr,
+ pj_sockaddr_print(origin, tmp_addr, sizeof(tmp_addr), 0));
+
+ } else {
+ pj_assert(!"Invalid address family");
+ return PJ_EAFNOTSUP;
+ }
+
+ if (sess_name)
+ pj_strdup(pool, &sdp->name, sess_name);
+ else
+ sdp->name = STR_SDP_NAME;
+
+ /* SDP time and attributes. */
+ sdp->time.start = sdp->time.stop = 0;
+ sdp->attr_count = 0;
+
/* Done */
*p_sdp = sdp;
return PJ_SUCCESS;
+}
+/**
+ * Create a SDP session description that describes the endpoint
+ * capability.
+ */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ unsigned stream_cnt,
+ const pjmedia_sock_info sock_info[],
+ pjmedia_sdp_session **p_sdp )
+{
+ const pj_sockaddr *addr0;
+ pjmedia_sdp_session *sdp;
+ pjmedia_sdp_media *m;
+ unsigned i;
+ pj_status_t status;
+
+ /* Sanity check arguments */
+ PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_cnt < PJMEDIA_MAX_SDP_MEDIA, PJ_ETOOMANY);
+
+ addr0 = &sock_info[0].rtp_addr_name;
+
+ /* Create and initialize basic SDP session */
+ status = pjmedia_endpt_create_base_sdp(endpt, pool, NULL, addr0, &sdp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Audio is first, by convention */
+ status = pjmedia_endpt_create_audio_sdp(endpt, pool,
+ &sock_info[0], 0, &m);
+ if (status != PJ_SUCCESS)
+ return status;
+ sdp->media[sdp->media_count++] = m;
+
+ /* The remaining stream, if any, are videos (by convention as well) */
+ for (i=1; i<stream_cnt; ++i) {
+ status = pjmedia_endpt_create_video_sdp(endpt, pool,
+ &sock_info[i], 0, &m);
+ if (status != PJ_SUCCESS)
+ return status;
+ sdp->media[sdp->media_count++] = m;
+ }
+
+ /* Done */
+ *p_sdp = sdp;
+
+ return PJ_SUCCESS;
}