pjmedia_sdp_media *Sdp::setMediaDescriptorLine() { pjmedia_sdp_media *med = PJ_POOL_ZALLOC_T(memPool_, pjmedia_sdp_media); med->desc.media = pj_str((char*)"audio"); med->desc.port_count = 1; med->desc.port = localAudioPort_; // in case of sdes, media are tagged as "RTP/SAVP", RTP/AVP elsewhere med->desc.transport = pj_str(srtpCrypto_.empty() ? (char*)"RTP/AVP" : (char*)"RTP/SAVP"); med->desc.fmt_count = codec_list_.size(); for (unsigned i=0; i<med->desc.fmt_count; i++) { sfl::Codec *codec = codec_list_[i]; std::ostringstream result; result << (int)codec->getPayloadType(); pj_strdup2(memPool_, &med->desc.fmt[i], result.str().c_str()); // Add a rtpmap field for each codec // We could add one only for dynamic payloads because the codecs with static RTP payloads // are entirely defined in the RFC 3351, but if we want to add other attributes like an asymmetric // connection, the rtpmap attribute will be useful to specify for which codec it is applicable pjmedia_sdp_rtpmap rtpmap; rtpmap.pt = med->desc.fmt[i]; rtpmap.enc_name = pj_str((char*) codec->getMimeSubtype().c_str()); // G722 require G722/8000 media description even if it is 16000 codec if (codec->getPayloadType() == 9) { rtpmap.clock_rate = 8000; } else { rtpmap.clock_rate = codec->getClockRate(); } rtpmap.param.ptr = ((char* const)""); rtpmap.param.slen = 0; pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr); med->attr[med->attr_count++] = attr; } med->attr[ med->attr_count++] = pjmedia_sdp_attr_create(memPool_, "sendrecv", NULL); if (!zrtpHelloHash_.empty()) addZrtpAttribute(med, zrtpHelloHash_); setTelephoneEventRtpmap(med); return med; }
static pjmedia_sdp_attr* generate_rtpmap_attr(pjmedia_sdp_media *media, pj_pool_t *pool, int rtp_code, int asterisk_format, struct ast_format *format, int code) { pjmedia_sdp_rtpmap rtpmap; pjmedia_sdp_attr *attr = NULL; char tmp[64]; snprintf(tmp, sizeof(tmp), "%d", rtp_code); pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; rtpmap.clock_rate = ast_rtp_lookup_sample_rate2(asterisk_format, format, code); pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, 0)); rtpmap.param.slen = 0; rtpmap.param.ptr = NULL; pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); return attr; }
static pjmedia_sdp_attr* generate_rtpmap_attr(struct ast_sip_session *session, pjmedia_sdp_media *media, pj_pool_t *pool, int rtp_code, int asterisk_format, struct ast_format *format, int code) { pjmedia_sdp_rtpmap rtpmap; pjmedia_sdp_attr *attr = NULL; char tmp[64]; enum ast_rtp_options options = session->endpoint->media.g726_non_standard ? AST_RTP_OPT_G726_NONSTANDARD : 0; snprintf(tmp, sizeof(tmp), "%d", rtp_code); pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; rtpmap.clock_rate = ast_rtp_lookup_sample_rate2(asterisk_format, format, code); pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, options)); rtpmap.param.slen = 0; rtpmap.param.ptr = NULL; pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); return attr; }
/** * 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 ) { pj_time_val tv; unsigned i; const pj_sockaddr *addr0; pjmedia_sdp_session *sdp; pjmedia_sdp_media *m; pjmedia_sdp_attr *attr; /* 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; /* SDP time and attributes. */ sdp->time.start = sdp->time.stop = 0; sdp->attr_count = 0; /* Create media stream 0: */ 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); 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) { attr = pjmedia_sdp_attr_create_rtcp(pool, &sock_info->rtcp_addr_name); if (attr) pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } #endif /* 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; if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED) break; codec_info = &endpt->codec_mgr.codec_desc[i].info; pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info, &codec_param); fmt = &m->desc.fmt[m->desc.fmt_count++]; fmt->ptr = (char*) pj_pool_alloc(pool, 8); fmt->slen = pj_utoa(codec_info->pt, fmt->ptr); rtpmap.pt = *fmt; rtpmap.enc_name = codec_info->encoding_name; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) if (codec_info->pt == PJMEDIA_RTP_PT_G722) rtpmap.clock_rate = 8000; else rtpmap.clock_rate = codec_info->clock_rate; #else rtpmap.clock_rate = codec_info->clock_rate; #endif /* For audio codecs, rtpmap parameters denotes the number * of channels, which can be omited if the value is 1. */ if (codec_info->type == PJMEDIA_TYPE_AUDIO && codec_info->channel_cnt > 1) { /* Can only support one digit channel count */ pj_assert(codec_info->channel_cnt < 10); tmp_param[0] = (char)('0' + codec_info->channel_cnt); rtpmap.param.ptr = tmp_param; rtpmap.param.slen = 1; } else { rtpmap.param.slen = 0; } if (codec_info->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.setting.dec_fmtp.cnt > 0) { enum { MAX_FMTP_STR_LEN = 160 }; char buf[MAX_FMTP_STR_LEN]; unsigned buf_len = 0, i; pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp; /* Print codec PT */ buf_len += pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN - buf_len, "%d", codec_info->pt); for (i = 0; i < dec_fmtp->cnt; ++i) { unsigned test_len = 2; /* Check if buf still available */ test_len = dec_fmtp->param[i].val.slen + dec_fmtp->param[i].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, (i == 0?" ":";")); /* Print an fmtp param */ if (dec_fmtp->param[i].name.slen) buf_len += pj_ansi_snprintf( &buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s=%.*s", (int)dec_fmtp->param[i].name.slen, dec_fmtp->param[i].name.ptr, (int)dec_fmtp->param[i].val.slen, dec_fmtp->param[i].val.ptr); else buf_len += pj_ansi_snprintf(&buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s", (int)dec_fmtp->param[i].val.slen, dec_fmtp->param[i].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; } } /* 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 /* * Add support telephony event */ m->desc.fmt[m->desc.fmt_count++] = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR); /* Add rtpmap. */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("rtpmap"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " telephone-event/8000"); m->attr[m->attr_count++] = attr; /* Add fmtp */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("fmtp"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15"); m->attr[m->attr_count++] = attr; #endif /* Done */ *p_sdp = sdp; 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; unsigned max_bitrate = 0; 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; pjmedia_video_format_detail *vfd; 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].packings & PJMEDIA_VID_PACKING_PACKETS) == 0 || 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; } /* Find maximum bitrate in this media */ vfd = pjmedia_format_get_video_format_detail(&codec_param.enc_fmt, PJ_TRUE); if (vfd && max_bitrate < vfd->max_bps) max_bitrate = vfd->max_bps; } /* Put bandwidth info in media level using bandwidth modifier "TIAS" * (RFC3890). */ if (max_bitrate) { const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; pjmedia_sdp_bandw *b; b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw); b->modifier = STR_BANDW_MODIFIER; b->value = max_bitrate; m->bandw[m->bandw_count++] = b; } *p_m = m; 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; unsigned max_bitrate = 0; 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_codec_param codec_param; pj_str_t *fmt; if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED) break; codec_info = &endpt->codec_mgr.codec_desc[i].info; pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info, &codec_param); fmt = &m->desc.fmt[m->desc.fmt_count++]; fmt->ptr = (char*) pj_pool_alloc(pool, 8); fmt->slen = pj_utoa(codec_info->pt, fmt->ptr); rtpmap.pt = *fmt; rtpmap.enc_name = codec_info->encoding_name; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) if (codec_info->pt == PJMEDIA_RTP_PT_G722) rtpmap.clock_rate = 8000; else rtpmap.clock_rate = codec_info->clock_rate; #else rtpmap.clock_rate = codec_info->clock_rate; #endif /* For audio codecs, rtpmap parameters denotes the number * of channels, which can be omited if the value is 1. */ if (codec_info->type == PJMEDIA_TYPE_AUDIO && codec_info->channel_cnt > 1) { /* Can only support one digit channel count */ pj_assert(codec_info->channel_cnt < 10); tmp_param[0] = (char)('0' + codec_info->channel_cnt); rtpmap.param.ptr = tmp_param; rtpmap.param.slen = 1; } else { rtpmap.param.ptr = ""; rtpmap.param.slen = 0; } if (codec_info->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.setting.dec_fmtp.cnt > 0) { enum { MAX_FMTP_STR_LEN = 160 }; char buf[MAX_FMTP_STR_LEN]; unsigned buf_len = 0, i; pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp; /* Print codec PT */ buf_len += pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN - buf_len, "%d", codec_info->pt); for (i = 0; i < dec_fmtp->cnt; ++i) { unsigned test_len = 2; /* Check if buf still available */ test_len = dec_fmtp->param[i].val.slen + dec_fmtp->param[i].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, (i == 0?" ":";")); /* Print an fmtp param */ if (dec_fmtp->param[i].name.slen) buf_len += pj_ansi_snprintf( &buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s=%.*s", (int)dec_fmtp->param[i].name.slen, dec_fmtp->param[i].name.ptr, (int)dec_fmtp->param[i].val.slen, dec_fmtp->param[i].val.ptr); else buf_len += pj_ansi_snprintf(&buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s", (int)dec_fmtp->param[i].val.slen, dec_fmtp->param[i].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; } /* Find maximum bitrate in this media */ if (max_bitrate < codec_param.info.max_bps) max_bitrate = codec_param.info.max_bps; } #if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \ PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0 /* * Add support telephony event */ if (endpt->has_telephone_event) { m->desc.fmt[m->desc.fmt_count++] = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR); /* Add rtpmap. */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("rtpmap"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " telephone-event/8000"); m->attr[m->attr_count++] = attr; /* Add fmtp */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("fmtp"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15"); m->attr[m->attr_count++] = attr; } #endif /* Put bandwidth info in media level using bandwidth modifier "TIAS" * (RFC3890). */ if (max_bitrate) { const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; pjmedia_sdp_bandw *b; b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw); b->modifier = STR_BANDW_MODIFIER; b->value = max_bitrate; m->bandw[m->bandw_count++] = b; } *p_m = m; 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 ) { pj_time_val tv; unsigned i; pjmedia_sdp_session *sdp; pjmedia_sdp_media *m; pjmedia_sdp_attr *attr; /* 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 (pool, sizeof(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; sdp->origin.addr_type = STR_IP4; pj_strdup2(pool, &sdp->origin.addr, pj_inet_ntoa(sock_info[0].rtp_addr_name.sin_addr)); 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 (pool, sizeof(pjmedia_sdp_conn)); sdp->conn->net_type = STR_IN; sdp->conn->addr_type = STR_IP4; pj_strdup2(pool, &sdp->conn->addr, pj_inet_ntoa(sock_info[0].rtp_addr_name.sin_addr)); /* SDP time and attributes. */ sdp->time.start = sdp->time.stop = 0; sdp->attr_count = 0; /* Create media stream 0: */ sdp->media_count = 1; m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media)); sdp->media[0] = m; /* Standard media info: */ pj_strdup(pool, &m->desc.media, &STR_AUDIO); m->desc.port = pj_ntohs(sock_info[0].rtp_addr_name.sin_port); 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 { attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr)); attr->name = pj_str("rtcp"); attr->value.ptr = pj_pool_alloc(pool, 80); attr->value.slen = pj_ansi_snprintf(attr->value.ptr, 80, "%u IN IP4 %s", pj_ntohs(sock_info[0].rtcp_addr_name.sin_port), pj_inet_ntoa(sock_info[0].rtcp_addr_name.sin_addr)); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } #endif /* 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; if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED) break; codec_info = &endpt->codec_mgr.codec_desc[i].info; pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info, &codec_param); fmt = &m->desc.fmt[m->desc.fmt_count++]; fmt->ptr = pj_pool_alloc(pool, 8); fmt->slen = pj_utoa(codec_info->pt, fmt->ptr); rtpmap.pt = *fmt; rtpmap.clock_rate = codec_info->clock_rate; rtpmap.enc_name = codec_info->encoding_name; /* For audio codecs, rtpmap parameters denotes the number * of channels, which can be omited if the value is 1. */ if (codec_info->type == PJMEDIA_TYPE_AUDIO && codec_info->channel_cnt > 1) { /* Can only support one digit channel count */ pj_assert(codec_info->channel_cnt < 10); tmp_param[0] = '/'; tmp_param[1] = (char)('0' + codec_info->channel_cnt); rtpmap.param.ptr = tmp_param; rtpmap.param.slen = 2; } else { rtpmap.param.slen = 0; } pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); m->attr[m->attr_count++] = attr; /* Add fmtp mode where applicable */ if (codec_param.setting.dec_fmtp_mode != 0) { const pj_str_t fmtp = { "fmtp", 4 }; attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr)); attr->name = fmtp; attr->value.ptr = pj_pool_alloc(pool, 32); attr->value.slen = pj_ansi_snprintf( attr->value.ptr, 32, "%d mode=%d", codec_info->pt, codec_param.setting.dec_fmtp_mode); m->attr[m->attr_count++] = attr; } } /* Add sendrecv attribute. */ attr = pj_pool_zalloc(pool, sizeof(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 /* * Add support telephony event */ m->desc.fmt[m->desc.fmt_count++] = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR); /* Add rtpmap. */ attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr)); attr->name = pj_str("rtpmap"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " telephone-event/8000"); m->attr[m->attr_count++] = attr; /* Add fmtp */ attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr)); attr->name = pj_str("fmtp"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15"); m->attr[m->attr_count++] = attr; #endif /* Done */ *p_sdp = sdp; return PJ_SUCCESS; }