PJ_DEF(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt) { #if PJ_LOG_MAX_LEVEL >= 3 unsigned i, count; pjmedia_codec_info codec_info[32]; unsigned prio[32]; PJ_LOG(3,(THIS_FILE, "Dumping PJMEDIA capabilities:")); count = PJ_ARRAY_SIZE(codec_info); if (pjmedia_codec_mgr_enum_codecs(&endpt->codec_mgr, &count, codec_info, prio) != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " -error: failed to enum codecs")); return PJ_SUCCESS; } PJ_LOG(3,(THIS_FILE, " Total number of installed codecs: %d", count)); for (i=0; i<count; ++i) { const char *type; pjmedia_codec_param param; char bps[32]; switch (codec_info[i].type) { case PJMEDIA_TYPE_AUDIO: type = "Audio"; break; case PJMEDIA_TYPE_VIDEO: type = "Video"; break; default: type = "Unknown type"; break; } if (pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, &codec_info[i], ¶m) != PJ_SUCCESS) { pj_bzero(¶m, sizeof(pjmedia_codec_param)); } PJ_LOG(3,(THIS_FILE, " %s codec #%2d: pt=%d (%.*s @%dKHz/%d, %sbps, %dms%s%s%s%s%s)", type, i, codec_info[i].pt, (int)codec_info[i].encoding_name.slen, codec_info[i].encoding_name.ptr, codec_info[i].clock_rate/1000, codec_info[i].channel_cnt, good_number(bps, param.info.avg_bps), param.info.frm_ptime * param.setting.frm_per_pkt, (param.setting.vad ? " vad" : ""), (param.setting.cng ? " cng" : ""), (param.setting.plc ? " plc" : ""), (param.setting.penh ? " penh" : ""), (prio[i]==PJMEDIA_CODEC_PRIO_DISABLED?" disabled":""))); } #endif 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; 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=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 stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { pjmedia_codec_mgr *mgr; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pjmedia_sdp_rtpmap *rtpmap; unsigned i, pt, fmti; pj_status_t status; /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Reset: */ pj_bzero(si, sizeof(*si)); #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR /* Set default RTCP XR enabled/disabled */ si->rtcp_xr_enabled = PJ_TRUE; #endif /* Media type: */ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) { si->type = PJMEDIA_TYPE_AUDIO; } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) { si->type = PJMEDIA_TYPE_VIDEO; } else { si->type = PJMEDIA_TYPE_UNKNOWN; /* Avoid rejecting call because of unrecognized media, * just return PJ_SUCCESS, this media will be deactivated later. */ //return PJMEDIA_EINVALIMEDIATYPE; return PJ_SUCCESS; } /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, but some UA sends telephone-event as fmt[0] and this would cause assert failure below. Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. // And codec must be numeric! if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || !pj_isdigit(*rem_m->desc.fmt[0].ptr)) { return PJMEDIA_EINVALIDPT; } pt = pj_strtoul(&local_m->desc.fmt[0]); pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); */ /* This is to suppress MSVC warning about uninitialized var */ pt = 0; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS ) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) /* The session info should have the actual clock rate, because * this info is used for calculationg buffer size, etc in stream */ if (si->fmt.pt == PJMEDIA_RTP_PT_G722) si->fmt.clock_rate = 16000; #endif /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; i<rem_m->desc.fmt_count; ++i) { unsigned rpt; pjmedia_sdp_attr *r_attr; pjmedia_sdp_rtpmap r_rtpmap; rpt = pj_strtoul(&rem_m->desc.fmt[i]); if (rpt < 96) continue; r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP, &rem_m->desc.fmt[i]); if (!r_attr) continue; if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS) continue; if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) && rtpmap->clock_rate == r_rtpmap.clock_rate) { /* Found matched codec. */ si->tx_pt = rpt; break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); /* Get remote fmtp for our encoder. */ parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp); /* Get local fmtp for our decoder. */ parse_fmtp(pool, local_m, si->fmt.pt, &si->param->setting.dec_fmtp); /* Get remote maxptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "maxptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; pj_strltrim(&tmp_val); si->tx_maxptime = pj_strtoul(&tmp_val); } /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: * http:// * * Thanks Alain Totouom */ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; i<local_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; i<rem_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return PJ_SUCCESS; }
static void pcap2wav(const pj_str_t *codec, const pj_str_t *wav_filename, pjmedia_aud_dev_index dev_id, const pj_str_t *srtp_crypto, const pj_str_t *srtp_key) { const pj_str_t WAV = {".wav", 4}; struct pkt { pj_uint8_t buffer[320]; pjmedia_rtp_hdr *rtp; pj_uint8_t *payload; unsigned payload_len; } pkt0; pjmedia_codec_mgr *cmgr; const pjmedia_codec_info *ci; pjmedia_codec_param param; unsigned samples_per_frame; pj_status_t status; /* Initialize all codecs */ T( pjmedia_codec_register_audio_codecs(app.mept, NULL) ); /* Create SRTP transport is needed */ #if PJMEDIA_HAS_SRTP if (srtp_crypto->slen) { pjmedia_srtp_crypto crypto; pj_bzero(&crypto, sizeof(crypto)); crypto.key = *srtp_key; crypto.name = *srtp_crypto; T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) ); T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) ); } #else PJ_UNUSED_ARG(srtp_crypto); PJ_UNUSED_ARG(srtp_key); #endif /* Read first packet */ read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, &pkt0.payload, &pkt0.payload_len, PJ_FALSE); cmgr = pjmedia_endpt_get_codec_mgr(app.mept); /* Get codec info and param for the specified payload type */ app.pt = pkt0.rtp->pt; if (app.pt >=0 && app.pt < 96) { T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) ); } else { unsigned cnt = 2; const pjmedia_codec_info *info[2]; T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt, info, NULL) ); if (cnt != 1) err_exit("Codec ID must be specified and unique!", 0); ci = info[0]; } T( pjmedia_codec_mgr_get_default_param(cmgr, ci, ¶m) ); /* Alloc and init codec */ T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) ); T( pjmedia_codec_init(app.codec, app.pool) ); T( pjmedia_codec_open(app.codec, ¶m) ); /* Init audio device or WAV file */ samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000; if (pj_strcmp2(wav_filename, "-") == 0) { pjmedia_aud_param aud_param; /* Open audio device */ T( pjmedia_aud_dev_default_param(dev_id, &aud_param) ); aud_param.dir = PJMEDIA_DIR_PLAYBACK; aud_param.channel_count = ci->channel_cnt; aud_param.clock_rate = ci->clock_rate; aud_param.samples_per_frame = samples_per_frame; T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, NULL, &app.aud_strm) ); T( pjmedia_aud_stream_start(app.aud_strm) ); } else if (pj_stristr(wav_filename, &WAV)) { /* Open WAV file */ T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr, ci->clock_rate, ci->channel_cnt, samples_per_frame, param.info.pcm_bits_per_sample, 0, 0, &app.wav) ); } else { err_exit("invalid output file", PJ_EINVAL); } /* Loop reading PCAP and writing WAV file */ for (;;) { struct pkt pkt1; pj_timestamp ts; pjmedia_frame frames[16], pcm_frame; short pcm[320]; unsigned i, frame_cnt; long samples_cnt, ts_gap; pj_assert(sizeof(pcm) >= samples_per_frame); /* Parse first packet */ ts.u64 = 0; frame_cnt = PJ_ARRAY_SIZE(frames); T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, &ts, &frame_cnt, frames) ); /* Decode and write to WAV file */ samples_cnt = 0; for (i=0; i<frame_cnt; ++i) { pjmedia_frame pcm_frame; pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; T( pjmedia_codec_decode(app.codec, &frames[i], (unsigned)pcm_frame.size, &pcm_frame) ); if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); } if (app.aud_strm) { T( wait_play(&pcm_frame) ); } samples_cnt += samples_per_frame; } /* Read next packet */ read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp, &pkt1.payload, &pkt1.payload_len, PJ_TRUE); /* Fill in the gap (if any) between pkt0 and pkt1 */ ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) - samples_cnt; while (ts_gap >= (long)samples_per_frame) { pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; if (app.codec->op->recover) { T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, &pcm_frame) ); } else { pj_bzero(pcm_frame.buf, pcm_frame.size); } if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); } if (app.aud_strm) { T( wait_play(&pcm_frame) ); } ts_gap -= samples_per_frame; } /* Next */ pkt0 = pkt1; pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer; pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer); } }
/* * Internal function for collecting codec info and param from the SDP media. */ static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_codec_mgr *mgr, const pjmedia_sdp_media *local_m, const pjmedia_sdp_media *rem_m) { const pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; unsigned i, fmti, pt = 0; pj_status_t status; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { pjmedia_sdp_rtpmap r; if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if (pt < 96) { /* This is known static PT. Skip rtpmap checking because it is * optional. */ break; } attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) continue; status = pjmedia_sdp_attr_get_rtpmap(attr, &r); if (status != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) != 0) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get payload type for receiving direction */ si->rx_pt = pt; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) /* The session info should have the actual clock rate, because * this info is used for calculationg buffer size, etc in stream */ if (si->fmt.pt == PJMEDIA_RTP_PT_G722) si->fmt.clock_rate = 16000; #endif /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { pjmedia_codec_id codec_id; pj_str_t codec_id_st; const pjmedia_codec_info *p_info; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); si->fmt.encoding_name = rtpmap->enc_name; si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } /* Normalize the codec info from codec manager. Note that the * payload type will be resetted to its default (it might have * been rewritten by the SDP negotiator to match to the remote * offer), this is intentional as currently some components may * prefer (or even require) the default PT in codec info. */ pjmedia_codec_info_to_id(&si->fmt, codec_id, sizeof(codec_id)); i = 1; codec_id_st = pj_str(codec_id); status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id_st, &i, &p_info, NULL); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; i<rem_m->desc.fmt_count; ++i) { if (pjmedia_sdp_neg_fmt_match(pool, (pjmedia_sdp_media*)local_m, fmti, (pjmedia_sdp_media*)rem_m, i, 0) == PJ_SUCCESS) { /* Found matched codec. */ si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]); break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); /* Get remote fmtp for our encoder. */ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp); /* Get local fmtp for our decoder. */ pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, &si->param->setting.dec_fmtp); if (!pj_stricmp2(&si->fmt.encoding_name, "opus")) { get_opus_channels_and_clock_rate(&si->param->setting.enc_fmtp, &si->param->setting.dec_fmtp, &si->fmt.channel_cnt, &si->fmt.clock_rate); } /* Get the remote ptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "ptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; unsigned frm_per_pkt; pj_strltrim(&tmp_val); /* Round up ptime when the specified is not multiple of frm_ptime */ frm_per_pkt = (pj_strtoul(&tmp_val) + si->param->info.frm_ptime/2) / si->param->info.frm_ptime; if (frm_per_pkt != 0) { si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt; } } /* Get remote maxptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "maxptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; pj_strltrim(&tmp_val); si->tx_maxptime = pj_strtoul(&tmp_val); } /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: * http:// * * Thanks Alain Totouom */ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; i<local_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; i<rem_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } return PJ_SUCCESS; }
/* * Encode test. Read input from WAV file, encode to temporary output file, * and compare the temporary output file to the reference file. */ static int codec_test_encode(pjmedia_codec_mgr *mgr, char *codec_name, unsigned bitrate, const char *wav_file, const char *ref_encoded_file) { pj_str_t codec_id = pj_str(codec_name); pj_pool_t *pool = NULL; unsigned count, samples_per_frame; pj_size_t encoded_frame_len = 0, pos; pjmedia_codec *codec = NULL; const pjmedia_codec_info *ci[1]; pjmedia_codec_param codec_param; pjmedia_port *wav_port = NULL; pjmedia_frame in_frame, out_frame; FILE *output = NULL, *fref = NULL; int rc = 0; pj_status_t status; pool = pj_pool_create(mem, "codec-vectors", 512, 512, NULL); if (!pool) { rc = -20; goto on_return; } /* Find and open the codec */ count = 1; status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id, &count, ci, NULL); if (status != PJ_SUCCESS) { rc = -30; goto on_return; } status = pjmedia_codec_mgr_alloc_codec(mgr, ci[0], &codec); if (status != PJ_SUCCESS) { rc = -40; goto on_return; } status = pjmedia_codec_mgr_get_default_param(mgr, ci[0], &codec_param); if (status != PJ_SUCCESS) { rc = -50; goto on_return; } codec_param.info.avg_bps = bitrate; codec_param.setting.vad = 0; /* For G7221, the bitrate is set via param.setting.dec_fmtp, if it has * no info about bitrate, the codec will check info.avg_bps. So, let's * just clear the SDP fmtp. */ if (pj_ansi_strstr(codec_name, "G7221/")) { codec_param.setting.dec_fmtp.cnt = 0; } status = pjmedia_codec_init(codec, pool); if (status != PJ_SUCCESS) { rc = -60; goto on_return; } status = pjmedia_codec_open(codec, &codec_param); if (status != PJ_SUCCESS) { rc = -70; goto on_return; } /* Open WAV file */ status = pjmedia_wav_player_port_create(pool, wav_file, codec_param.info.frm_ptime, PJMEDIA_FILE_NO_LOOP, 0, &wav_port); if (status != PJ_SUCCESS) { rc = -80; goto on_return; } /* Open output file */ output = fopen(TMP_OUT, "wb"); if (!output) { rc = -90; goto on_return; } /* Allocate buffer for PCM and encoded frames */ samples_per_frame = codec_param.info.clock_rate * codec_param.info.frm_ptime / 1000; in_frame.buf = pj_pool_alloc(pool, samples_per_frame * 2); out_frame.buf = (pj_uint8_t*) pj_pool_alloc(pool, samples_per_frame); /* Loop read WAV file and encode and write to output file */ for (;;) { in_frame.size = samples_per_frame * 2; in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; status = pjmedia_port_get_frame(wav_port, &in_frame); if (status != PJ_SUCCESS || in_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) break; out_frame.size = samples_per_frame; status = pjmedia_codec_encode(codec, &in_frame, samples_per_frame, &out_frame); if (status != PJ_SUCCESS) { rc = -95; goto on_return; } if (out_frame.size) { fwrite(out_frame.buf, out_frame.size, 1, output); if (encoded_frame_len == 0) encoded_frame_len = out_frame.size; } } fclose(output); output = NULL; /* Compare encoded files */ fref = fopen(ref_encoded_file, "rb"); if (!fref) { rc = -100; goto on_return; } output = fopen(TMP_OUT, "rb"); if (!output) { rc = -110; goto on_return; } pos = 0; for (;;) { pj_size_t count; count = fread(in_frame.buf, encoded_frame_len, 1, fref); if (count != 1) break; count = fread(out_frame.buf, encoded_frame_len, 1, output); if (count != 1) break; if (memcmp(in_frame.buf, out_frame.buf, encoded_frame_len)) { unsigned i; pj_uint8_t *in = (pj_uint8_t*)in_frame.buf; pj_uint8_t *out = (pj_uint8_t*)out_frame.buf; for (i=0; i<encoded_frame_len; ++i) { if (in[i] != out[i]) break; } PJ_LOG(1,(THIS_FILE," failed: mismatch at pos %d", pos+i)); rc = -200; break; } pos += encoded_frame_len; } on_return: if (output) fclose(output); if (fref) fclose(fref); if (codec) { pjmedia_codec_close(codec); pjmedia_codec_mgr_dealloc_codec(mgr, codec); } if (wav_port) pjmedia_port_destroy(wav_port); if (pool) pj_pool_release(pool); return rc; }
/* * Decode test * * Decode the specified encoded file in "in_encoded_file" into temporary * PCM output file, and compare the temporary PCM output file with * the PCM reference file. * * Some reference file requires manipulation to the PCM output * before comparison, such manipulation can be done by supplying * this function with the "manip" function. */ static int codec_test_decode(pjmedia_codec_mgr *mgr, char *codec_name, unsigned bitrate, unsigned encoded_len, const char *in_encoded_file, const char *ref_pcm_file, void (*manip)(short *pcm, unsigned count)) { pj_str_t codec_id = pj_str(codec_name); pj_pool_t *pool = NULL; unsigned count, samples_per_frame, pos; pjmedia_codec *codec = NULL; const pjmedia_codec_info *ci[1]; pjmedia_codec_param codec_param; pjmedia_frame out_frame; void *pkt; FILE *input = NULL, *output = NULL, *fref = NULL; pj_bool_t is_itu_format = PJ_FALSE; int rc = 0; pj_status_t status; pool = pj_pool_create(mem, "codec-vectors", 512, 512, NULL); if (!pool) { rc = -20; goto on_return; } /* Find and open the codec */ count = 1; status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id, &count, ci, NULL); if (status != PJ_SUCCESS) { rc = -30; goto on_return; } status = pjmedia_codec_mgr_alloc_codec(mgr, ci[0], &codec); if (status != PJ_SUCCESS) { rc = -40; goto on_return; } status = pjmedia_codec_mgr_get_default_param(mgr, ci[0], &codec_param); if (status != PJ_SUCCESS) { rc = -50; goto on_return; } codec_param.info.avg_bps = bitrate; codec_param.setting.vad = 0; /* For G7221, the bitrate is set via param.setting.dec_fmtp, if it has * no info about bitrate, the codec will check info.avg_bps. So, let's * just clear the SDP fmtp. */ if (pj_ansi_strstr(codec_name, "G7221/")) { codec_param.setting.dec_fmtp.cnt = 0; } status = pjmedia_codec_init(codec, pool); if (status != PJ_SUCCESS) { rc = -60; goto on_return; } status = pjmedia_codec_open(codec, &codec_param); if (status != PJ_SUCCESS) { rc = -70; goto on_return; } /* Open input file */ input = fopen(in_encoded_file, "rb"); if (!input) { rc = -80; goto on_return; } /* Is the file in ITU format? */ is_itu_format = pj_ansi_stricmp(in_encoded_file+strlen(in_encoded_file)-4, ".itu")==0; /* Open output file */ output = fopen(TMP_OUT, "wb"); if (!output) { rc = -90; goto on_return; } /* Allocate buffer for PCM and encoded frames */ samples_per_frame = codec_param.info.clock_rate * codec_param.info.frm_ptime / 1000; pkt = pj_pool_alloc(pool, samples_per_frame * 2); out_frame.buf = (pj_uint8_t*) pj_pool_alloc(pool, samples_per_frame * 2); /* Loop read WAV file and encode and write to output file */ for (;;) { pjmedia_frame in_frame[2]; pj_timestamp ts; unsigned count; pj_bool_t has_frame; if (is_itu_format) { int nsamp; short frame_err = 0; nsamp = read_ITU_format(input, (short*)pkt, &frame_err, encoded_len / 2, PJ_TRUE); if (nsamp != (int)encoded_len / 2) break; has_frame = !frame_err; } else { if (fread(pkt, encoded_len, 1, input) != 1) break; has_frame = PJ_TRUE; } if (has_frame) { count = 2; if (pjmedia_codec_parse(codec, pkt, encoded_len, &ts, &count, in_frame) != PJ_SUCCESS) { rc = -100; goto on_return; } if (count != 1) { rc = -110; goto on_return; } if (pjmedia_codec_decode(codec, &in_frame[0], samples_per_frame*2, &out_frame) != PJ_SUCCESS) { rc = -120; goto on_return; } } else { if (pjmedia_codec_recover(codec, samples_per_frame*2, &out_frame) != PJ_SUCCESS) { rc = -125; goto on_return; } } if (manip) manip((short*)out_frame.buf, samples_per_frame); if (fwrite(out_frame.buf, out_frame.size, 1, output) != 1) { rc = -130; goto on_return; } } fclose(input); input = NULL; fclose(output); output = NULL; /* Compare encoded files */ fref = fopen(ref_pcm_file, "rb"); if (!fref) { rc = -140; goto on_return; } output = fopen(TMP_OUT, "rb"); if (!output) { rc = -110; goto on_return; } pos = 0; for (;;) { pj_size_t count; count = fread(pkt, samples_per_frame*2, 1, fref); if (count != 1) break; count = fread(out_frame.buf, samples_per_frame*2, 1, output); if (count != 1) break; if (memcmp(pkt, out_frame.buf, samples_per_frame*2)) { unsigned i; pj_int16_t *in = (pj_int16_t*)pkt; pj_int16_t *out = (pj_int16_t*)out_frame.buf; for (i=0; i<samples_per_frame; ++i) { if (in[i] != out[i]) break; } PJ_LOG(1,(THIS_FILE," failed: mismatch at samples %d", pos+i)); rc = -200; break; } pos += samples_per_frame; } on_return: if (output) fclose(output); if (fref) fclose(fref); if (input) fclose(input); if (codec) { pjmedia_codec_close(codec); pjmedia_codec_mgr_dealloc_codec(mgr, codec); } if (pool) pj_pool_release(pool); return rc; }
/* * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *rec_file_port = NULL, *play_file_port = NULL; pjmedia_master_port *master_port = NULL; pjmedia_snd_port *snd_port = NULL; pjmedia_stream *stream = NULL; pjmedia_port *stream_port; char tmp[10]; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP variables */ pj_bool_t use_srtp = PJ_FALSE; char tmp_tx_key[64]; char tmp_rx_key[64]; pj_str_t srtp_tx_key = {NULL, 0}; pj_str_t srtp_rx_key = {NULL, 0}; pj_str_t srtp_crypto_suite = {NULL, 0}; int tmp_key_len; #endif /* Default values */ const pjmedia_codec_info *codec_info; pjmedia_codec_param codec_param; pjmedia_dir dir = PJMEDIA_DIR_DECODING; pj_sockaddr_in remote_addr; pj_uint16_t local_port = 4000; char *codec_id = NULL; char *rec_file = NULL; char *play_file = NULL; enum { OPT_CODEC = 'c', OPT_LOCAL_PORT = 'p', OPT_REMOTE = 'r', OPT_PLAY_FILE = 'w', OPT_RECORD_FILE = 'R', OPT_SEND_RECV = 'b', OPT_SEND_ONLY = 's', OPT_RECV_ONLY = 'i', #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) OPT_USE_SRTP = 'S', #endif OPT_SRTP_TX_KEY = 'x', OPT_SRTP_RX_KEY = 'y', OPT_HELP = 'h', }; struct pj_getopt_option long_options[] = { { "codec", 1, 0, OPT_CODEC }, { "local-port", 1, 0, OPT_LOCAL_PORT }, { "remote", 1, 0, OPT_REMOTE }, { "play-file", 1, 0, OPT_PLAY_FILE }, { "record-file", 1, 0, OPT_RECORD_FILE }, { "send-recv", 0, 0, OPT_SEND_RECV }, { "send-only", 0, 0, OPT_SEND_ONLY }, { "recv-only", 0, 0, OPT_RECV_ONLY }, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) { "use-srtp", 2, 0, OPT_USE_SRTP }, { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY }, { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY }, #endif { "help", 0, 0, OPT_HELP }, { NULL, 0, 0, 0 }, }; int c; int option_index; pj_bzero(&remote_addr, sizeof(remote_addr)); /* init PJLIB : */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Parse arguments */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) { switch (c) { case OPT_CODEC: codec_id = pj_optarg; break; case OPT_LOCAL_PORT: local_port = (pj_uint16_t) atoi(pj_optarg); if (local_port < 1) { printf("Error: invalid local port %s\n", pj_optarg); return 1; } break; case OPT_REMOTE: { pj_str_t ip = pj_str(strtok(pj_optarg, ":")); pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":")); status = pj_sockaddr_in_init(&remote_addr, &ip, port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Invalid remote address", status); return 1; } } break; case OPT_PLAY_FILE: play_file = pj_optarg; break; case OPT_RECORD_FILE: rec_file = pj_optarg; break; case OPT_SEND_RECV: dir = PJMEDIA_DIR_ENCODING_DECODING; break; case OPT_SEND_ONLY: dir = PJMEDIA_DIR_ENCODING; break; case OPT_RECV_ONLY: dir = PJMEDIA_DIR_DECODING; break; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) case OPT_USE_SRTP: use_srtp = PJ_TRUE; if (pj_optarg) { pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg)); } else { srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80"); } break; case OPT_SRTP_TX_KEY: tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, (int)strlen(pj_optarg)); pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2); break; case OPT_SRTP_RX_KEY: tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, (int)strlen(pj_optarg)); pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2); break; #endif case OPT_HELP: usage(); return 1; default: printf("Invalid options %s\n", argv[pj_optind]); return 1; } } /* Verify arguments. */ if (dir & PJMEDIA_DIR_ENCODING) { if (remote_addr.sin_addr.s_addr == 0) { printf("Error: remote address must be set\n"); return 1; } } if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) { printf("Direction is set to --send-only because of --play-file\n"); dir = PJMEDIA_DIR_ENCODING; } #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP validation */ if (use_srtp) { if (!srtp_tx_key.slen || !srtp_rx_key.slen) { printf("Error: Key for each SRTP stream direction must be set\n"); return 1; } } #endif /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for application purpose */ pool = pj_pool_create( &cp.factory, /* pool factory */ "app", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); /* Register all supported codecs */ status = init_codecs(med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Find which codec to use. */ if (codec_id) { unsigned count = 1; pj_str_t str_codec_id = pj_str(codec_id); pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt); status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr, &str_codec_id, &count, &codec_info, NULL); if (status != PJ_SUCCESS) { printf("Error: unable to find codec %s\n", codec_id); return 1; } } else { /* Default to pcmu */ pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt), 0, &codec_info); } /* Create stream based on program arguments */ status = create_stream(pool, med_endpt, codec_info, dir, local_port, &remote_addr, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) use_srtp, &srtp_crypto_suite, &srtp_tx_key, &srtp_rx_key, #endif &stream); if (status != PJ_SUCCESS) goto on_exit; /* Get codec default param for info */ status = pjmedia_codec_mgr_get_default_param( pjmedia_endpt_get_codec_mgr(med_endpt), codec_info, &codec_param); /* Should be ok, as create_stream() above succeeded */ pj_assert(status == PJ_SUCCESS); /* Get the port interface of the stream */ status = pjmedia_stream_get_port( stream, &stream_port); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); if (play_file) { unsigned wav_ptime; wav_ptime = PJMEDIA_PIA_PTIME(&stream_port->info); status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime, 0, -1, &play_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); goto on_exit; } status = pjmedia_master_port_create(pool, play_file_port, stream_port, 0, &master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create master port", status); goto on_exit; } status = pjmedia_master_port_start(master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error starting master port", status); goto on_exit; } printf("Playing from WAV file %s..\n", play_file); } else if (rec_file) { status = pjmedia_wav_writer_port_create(pool, rec_file, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, 0, &rec_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); goto on_exit; } status = pjmedia_master_port_create(pool, stream_port, rec_file_port, 0, &master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create master port", status); goto on_exit; } status = pjmedia_master_port_start(master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error starting master port", status); goto on_exit; } printf("Recording to WAV file %s..\n", rec_file); } else { /* Create sound device port. */ if (dir == PJMEDIA_DIR_ENCODING_DECODING) status = pjmedia_snd_port_create(pool, -1, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else if (dir == PJMEDIA_DIR_ENCODING) status = pjmedia_snd_port_create_rec(pool, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else status = pjmedia_snd_port_create_player(pool, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create sound port", status); goto on_exit; } /* Connect sound port to stream */ status = pjmedia_snd_port_connect( snd_port, stream_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Start streaming */ pjmedia_stream_start(stream); /* Done */ if (dir == PJMEDIA_DIR_DECODING) printf("Stream is active, dir is recv-only, local port is %d\n", local_port); else if (dir == PJMEDIA_DIR_ENCODING) printf("Stream is active, dir is send-only, sending to %s:%d\n", pj_inet_ntoa(remote_addr.sin_addr), pj_ntohs(remote_addr.sin_port)); else printf("Stream is active, send/recv, local port is %d, " "sending to %s:%d\n", local_port, pj_inet_ntoa(remote_addr.sin_addr), pj_ntohs(remote_addr.sin_port)); for (;;) { puts(""); puts("Commands:"); puts(" s Display media statistics"); puts(" q Quit"); puts(""); printf("Command: "); fflush(stdout); if (fgets(tmp, sizeof(tmp), stdin) == NULL) { puts("EOF while reading stdin, will quit now.."); break; } if (tmp[0] == 's') print_stream_stat(stream, &codec_param); else if (tmp[0] == 'q') break; } /* Start deinitialization: */ on_exit: /* Destroy sound device */ if (snd_port) { pjmedia_snd_port_destroy( snd_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* If there is master port, then we just need to destroy master port * (it will recursively destroy upstream and downstream ports, which * in this case are file_port and stream_port). */ if (master_port) { pjmedia_master_port_destroy(master_port, PJ_TRUE); play_file_port = NULL; stream = NULL; } /* Destroy stream */ if (stream) { pjmedia_transport *tp; tp = pjmedia_stream_get_transport(stream); pjmedia_stream_destroy(stream); pjmedia_transport_close(tp); } /* Destroy file ports */ if (play_file_port) pjmedia_port_destroy( play_file_port ); if (rec_file_port) pjmedia_port_destroy( rec_file_port ); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); return (status == PJ_SUCCESS) ? 0 : 1; }
/* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { pjmedia_codec_mgr *mgr; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; pjmedia_sdp_rtpmap *rtpmap; int local_fmtp_mode = 0, rem_fmtp_mode = 0; unsigned i, pt, fmti; pj_status_t status; /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Reset: */ pj_bzero(si, sizeof(*si)); /* Media type: */ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) { si->type = PJMEDIA_TYPE_AUDIO; } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) { si->type = PJMEDIA_TYPE_VIDEO; } else { si->type = PJMEDIA_TYPE_UNKNOWN; return PJMEDIA_EINVALIMEDIATYPE; } /* Transport type must be equal */ if (pj_stricmp(&rem_m->desc.transport, &local_m->desc.transport) != 0) { si->type = PJMEDIA_TYPE_UNKNOWN; return PJMEDIA_SDPNEG_EINVANSTP; } /* Media direction: */ if (local_m->desc.port == 0 || pj_inet_addr(&local_conn->addr).s_addr==0 || pj_inet_addr(&rem_conn->addr).s_addr==0 || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* Set remote address: */ status = pj_sockaddr_in_init(&si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_in_init(&si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_in_init(&si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); si->rem_rtcp.sin_addr.s_addr = si->rem_addr.sin_addr.s_addr; } } } if (si->rem_rtcp.sin_addr.s_addr == 0) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr_in)); rtcp_port = pj_ntohs(si->rem_addr.sin_port) + 1; si->rem_rtcp.sin_port = pj_htons((pj_uint16_t)rtcp_port); } /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, but some UA sends telephone-event as fmt[0] and this would cause assert failure below. Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. // And codec must be numeric! if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || !pj_isdigit(*rem_m->desc.fmt[0].ptr)) { return PJMEDIA_EINVALIDPT; } pt = pj_strtoul(&local_m->desc.fmt[0]); pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); */ /* This is to suppress MSVC warning about uninitialized var */ pt = 0; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS ) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { if (rtpmap->param.slen == 2) { si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0'; } else { pj_str_t cnt; cnt.ptr = rtpmap->param.ptr + 1; cnt.slen = rtpmap->param.slen - 1; si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt); } } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { if (rtpmap->param.slen == 2) { si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0'; } else { pj_str_t cnt; cnt.ptr = rtpmap->param.ptr + 1; cnt.slen = rtpmap->param.slen - 1; si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt); } } else { si->fmt.channel_cnt = 1; } /* Get fmtp mode= param in local SDP, if any */ get_fmtp_mode(local_m, &local_m->desc.fmt[fmti], &local_fmtp_mode); /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; i<rem_m->desc.fmt_count; ++i) { unsigned rpt; pjmedia_sdp_attr *r_attr; pjmedia_sdp_rtpmap r_rtpmap; rpt = pj_strtoul(&rem_m->desc.fmt[i]); if (rpt < 96) continue; r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP, &rem_m->desc.fmt[i]); if (!r_attr) continue; if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS) continue; if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) && rtpmap->clock_rate == r_rtpmap.clock_rate) { /* Found matched codec. */ si->tx_pt = rpt; /* Get fmtp mode param in remote SDP, if any */ get_fmtp_mode(rem_m, &rtpmap->pt, &rem_fmtp_mode); break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = pj_pool_alloc(pool, sizeof(*si->param)); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); if (status != PJ_SUCCESS) return status; /* Set fmtp mode for both local and remote */ if (local_fmtp_mode != 0) si->param->setting.dec_fmtp_mode = (pj_int8_t)local_fmtp_mode; if (rem_fmtp_mode != 0) si->param->setting.enc_fmtp_mode = (pj_int8_t)rem_fmtp_mode; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; i<local_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; i<rem_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; 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; }
static pj_status_t enc_dec_test(const char *codec_id, const char *filein, const char *fileout) { pj_pool_t *pool; pjmedia_codec_mgr *cm; pjmedia_codec *codec; const pjmedia_codec_info *pci; pjmedia_codec_param param; unsigned cnt, samples_per_frame; pj_str_t tmp; pjmedia_port *wavin, *wavout; unsigned lost_pct; pj_status_t status; #define T file_msec_duration/1000, file_msec_duration%1000 pool = pjmedia_endpt_create_pool(mept, "encdec", 1000, 1000); cm = pjmedia_endpt_get_codec_mgr(mept); #ifdef LOST_PCT lost_pct = LOST_PCT; #else lost_pct = 0; #endif cnt = 1; CHECK( pjmedia_codec_mgr_find_codecs_by_id(cm, pj_cstr(&tmp, codec_id), &cnt, &pci, NULL) ); CHECK( pjmedia_codec_mgr_get_default_param(cm, pci, ¶m) ); samples_per_frame = param.info.clock_rate * param.info.frm_ptime / 1000; /* Control VAD */ param.setting.vad = 1; /* Open wav for reading */ CHECK( pjmedia_wav_player_port_create(pool, filein, param.info.frm_ptime, PJMEDIA_FILE_NO_LOOP, 0, &wavin) ); /* Open wav for writing */ CHECK( pjmedia_wav_writer_port_create(pool, fileout, param.info.clock_rate, param.info.channel_cnt, samples_per_frame, 16, 0, 0, &wavout) ); /* Alloc codec */ CHECK( pjmedia_codec_mgr_alloc_codec(cm, pci, &codec) ); CHECK( codec->op->init(codec, pool) ); CHECK( codec->op->open(codec, ¶m) ); for (;;) { pjmedia_frame frm_pcm, frm_bit, out_frm, frames[4]; pj_int16_t pcmbuf[320]; pj_timestamp ts; pj_uint8_t bitstream[160]; frm_pcm.buf = (char*)pcmbuf; frm_pcm.size = samples_per_frame * 2; /* Read from WAV */ if (pjmedia_port_get_frame(wavin, &frm_pcm) != PJ_SUCCESS) break; if (frm_pcm.type != PJMEDIA_FRAME_TYPE_AUDIO) break;; /* Update duration */ file_msec_duration += samples_per_frame * 1000 / param.info.clock_rate; /* Encode */ frm_bit.buf = bitstream; frm_bit.size = sizeof(bitstream); CHECK(codec->op->encode(codec, &frm_pcm, sizeof(bitstream), &frm_bit)); /* On DTX, write zero frame to wavout to maintain duration */ if (frm_bit.size == 0 || frm_bit.type != PJMEDIA_FRAME_TYPE_AUDIO) { out_frm.buf = (char*)pcmbuf; out_frm.size = 160; CHECK( pjmedia_port_put_frame(wavout, &out_frm) ); TRACE_((THIS_FILE, "%d.%03d read: %u, enc: %u", T, frm_pcm.size, frm_bit.size)); continue; } /* Parse the bitstream (not really necessary for this case * since we always decode 1 frame, but it's still good * for testing) */ ts.u64 = 0; cnt = PJ_ARRAY_SIZE(frames); CHECK( codec->op->parse(codec, bitstream, frm_bit.size, &ts, &cnt, frames) ); CHECK( (cnt==1 ? PJ_SUCCESS : -1) ); /* Decode or simulate packet loss */ out_frm.buf = (char*)pcmbuf; out_frm.size = sizeof(pcmbuf); if ((pj_rand() % 100) < (int)lost_pct) { /* Simulate loss */ CHECK( codec->op->recover(codec, sizeof(pcmbuf), &out_frm) ); TRACE_((THIS_FILE, "%d.%03d Packet lost", T)); } else { /* Decode */ CHECK( codec->op->decode(codec, &frames[0], sizeof(pcmbuf), &out_frm) ); } /* Write to WAV */ CHECK( pjmedia_port_put_frame(wavout, &out_frm) ); TRACE_((THIS_FILE, "%d.%03d read: %u, enc: %u, dec/write: %u", T, frm_pcm.size, frm_bit.size, out_frm.size)); } /* Close wavs */ pjmedia_port_destroy(wavout); pjmedia_port_destroy(wavin); /* Close codec */ codec->op->close(codec); pjmedia_codec_mgr_dealloc_codec(cm, codec); /* Release pool */ pj_pool_release(pool); return PJ_SUCCESS; }
/** * Create new session. */ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, const pjmedia_session_info *si, pjmedia_transport *transports[], void *user_data, pjmedia_session **p_session ) { pj_pool_t *pool; pjmedia_session *session; int i; /* Must be signed */ pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(endpt && si && p_session, PJ_EINVAL); /* Create pool for the session. */ pool = pjmedia_endpt_create_pool( endpt, "session%p", PJMEDIA_SESSION_SIZE, PJMEDIA_SESSION_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); session = PJ_POOL_ZALLOC_T(pool, pjmedia_session); session->pool = pool; session->endpt = endpt; session->stream_cnt = si->stream_cnt; session->user_data = user_data; /* Copy stream info (this simple memcpy may break sometime) */ pj_memcpy(session->stream_info, si->stream_info, si->stream_cnt * sizeof(pjmedia_stream_info)); /* Clone codec param and format info */ for (i=0; i<(int)si->stream_cnt; ++i) { pj_strdup(pool, &session->stream_info[i].fmt.encoding_name, &si->stream_info[i].fmt.encoding_name); if (session->stream_info[i].param) { session->stream_info[i].param = pjmedia_codec_param_clone(pool, si->stream_info[i].param); } else { pjmedia_codec_param cp; status = pjmedia_codec_mgr_get_default_param( pjmedia_endpt_get_codec_mgr(endpt), &si->stream_info[i].fmt, &cp); if (status != PJ_SUCCESS) return status; session->stream_info[i].param = pjmedia_codec_param_clone(pool, &cp); } } /* * Now create and start the stream! */ for (i=0; i<(int)si->stream_cnt; ++i) { /* Create the stream */ status = pjmedia_stream_create(endpt, session->pool, &session->stream_info[i], (transports?transports[i]:NULL), session, &session->stream[i]); if (status == PJ_SUCCESS) status = pjmedia_stream_start(session->stream[i]); if (status != PJ_SUCCESS) { for ( --i; i>=0; ++i) { pjmedia_stream_destroy(session->stream[i]); } pj_pool_release(session->pool); return status; } } /* Done. */ *p_session = session; return PJ_SUCCESS; }
JNIEXPORT jint JNICALL Java_org_sipdroid_pjlib_codec_open (JNIEnv *env, jclass cls, jstring codec_id) { int rc = 0; unsigned count = 1; LOGE("Intializing PJLIB..."); pj_init(); pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); pj_log_set_level(3); mem = &caching_pool.factory; char* ctmp; jboolean iscopy; const char *codec_str = env->GetStringUTFChars(codec_id, &iscopy); ctmp = const_cast<char*>(codec_str); const pj_str_t pj_codec_str = pj_str(ctmp); unsigned clock_rate = 8000; LOGE("pj_pool_create"); pool = pj_pool_create(mem, "pool", 1024, 1024, NULL); LOGE("PJ_POOL_ZALLOC_T"); cp = PJ_POOL_ZALLOC_T(pool, struct codec_port); LOGE("pjmedia_endpt_create"); status = pjmedia_endpt_create(mem, NULL, 0, &cp->endpt); if (status != PJ_SUCCESS) return NULL; cm = pjmedia_endpt_get_codec_mgr(cp->endpt); #if PJMEDIA_HAS_G711_CODEC CHECK( pjmedia_codec_g711_init(cp->endpt) ); #endif #if PJMEDIA_HAS_GSM_CODEC CHECK( pjmedia_codec_gsm_init(cp->endpt) ); #endif #if PJMEDIA_HAS_ILBC_CODEC CHECK( pjmedia_codec_ilbc_init(cp->endpt, 30) ); #endif #if PJMEDIA_HAS_SPEEX_CODEC CHECK( pjmedia_codec_speex_init(cp->endpt, 0, 5, 5) ); #endif #if PJMEDIA_HAS_G722_CODEC CHECK( pjmedia_codec_g722_init(cp->endpt) ); #endif LOGE("pjmedia_codec_mgr_find_codecs_by_id: %s", codec_str); CHECK( status = pjmedia_codec_mgr_find_codecs_by_id(cm, &pj_codec_str, &count, &ci, NULL) ); if (status != PJ_SUCCESS) { LOGE("Cannot find codec"); return NULL; } LOGE("pjmedia_codec_mgr_get_default_param"); CHECK( status = pjmedia_codec_mgr_get_default_param(cm, ci, ¶m) ); if (status != PJ_SUCCESS) { LOGE("pjmedia_codec_mgr_get_default_param failed"); return NULL; } //param.setting.vad = 1; LOGE("pjmedia_codec_mgr_alloc_codec"); CHECK( status = pjmedia_codec_mgr_alloc_codec(cm, ci, &cp->codec) ); if (status != PJ_SUCCESS) { LOGE("Cannot allocate codec"); return NULL; } LOGE("codec->op->init"); // channels=%d frm_ptime=%s", ci->channel_cnt, param.info.frm_ptime); status = (*cp->codec->op->init)(cp->codec, pool); if (status != PJ_SUCCESS) return NULL; LOGE("codec->op->open"); status = cp->codec->op->open(cp->codec, ¶m); if (status != PJ_SUCCESS) return NULL; samples_per_frame = param.info.clock_rate * param.info.frm_ptime / 1000; LOGE("Finished initializing codec..."); LOGE(" -> clock_rate=%d channel_count=%d samples_per_frame=%d pcm_bits_per_sample=%d", param.info.clock_rate, param.info.channel_cnt, \ samples_per_frame, param.info.pcm_bits_per_sample); return (jint)PJ_SUCCESS; }
static pj_status_t stream_init(const struct stream_cfg *cfg, struct stream **p_stream) { pj_pool_t *pool = NULL; struct stream *stream = NULL; pjmedia_codec_mgr *cm; unsigned count; const pjmedia_codec_info *ci; pjmedia_stream_info si; pj_status_t status; /* Create instance */ pool = pj_pool_create(&g_app.cp.factory, cfg->name, 512, 512, NULL); stream = PJ_POOL_ZALLOC_T(pool, struct stream); stream->pool = pool; /* Create stream info */ pj_bzero(&si, sizeof(si)); si.type = PJMEDIA_TYPE_AUDIO; si.proto = PJMEDIA_TP_PROTO_RTP_AVP; si.dir = cfg->dir; pj_sockaddr_in_init(&si.rem_addr.ipv4, NULL, 4000); /* dummy */ pj_sockaddr_in_init(&si.rem_rtcp.ipv4, NULL, 4001); /* dummy */ /* Apply JB settings if this is RX direction */ if (cfg->dir == PJMEDIA_DIR_DECODING) { si.jb_init = g_app.cfg.rx_jb_init; si.jb_min_pre = g_app.cfg.rx_jb_min_pre; si.jb_max_pre = g_app.cfg.rx_jb_max_pre; si.jb_max = g_app.cfg.rx_jb_max; } /* Get the codec info and param */ cm = pjmedia_endpt_get_codec_mgr(g_app.endpt); count = 1; status = pjmedia_codec_mgr_find_codecs_by_id(cm, &cfg->codec, &count, &ci, NULL); if (status != PJ_SUCCESS) { jbsim_perror("Unable to find codec", status); goto on_error; } pj_memcpy(&si.fmt, ci, sizeof(*ci)); si.param = PJ_POOL_ALLOC_T(pool, struct pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(cm, &si.fmt, si.param); if (status != PJ_SUCCESS) { jbsim_perror("Unable to get codec defaults", status); goto on_error; } si.tx_pt = si.fmt.pt; /* Apply ptime setting */ if (cfg->ptime) { si.param->setting.frm_per_pkt = (pj_uint8_t) ((cfg->ptime + si.param->info.frm_ptime - 1) / si.param->info.frm_ptime); } /* Apply DTX setting */ si.param->setting.vad = cfg->dtx; /* Apply PLC setting */ si.param->setting.plc = cfg->plc; /* Create stream */ status = pjmedia_stream_create(g_app.endpt, pool, &si, g_app.loop, NULL, &stream->strm); if (status != PJ_SUCCESS) { jbsim_perror("Error creating stream", status); goto on_error; } status = pjmedia_stream_get_port(stream->strm, &stream->port); if (status != PJ_SUCCESS) { jbsim_perror("Error retrieving stream", status); goto on_error; } /* Start stream */ status = pjmedia_stream_start(stream->strm); if (status != PJ_SUCCESS) { jbsim_perror("Error starting stream", status); goto on_error; } /* Done */ *p_stream = stream; return PJ_SUCCESS; on_error: if (stream) { stream_destroy(stream); } else { if (pool) pj_pool_release(pool); } return status; }