PJSIP的DTMF简单实现,回调函数:on_dtmf_digit

本文深入解析了DTMF(双音多频)原理及其实现,具体讨论了如何在PJSIP源码中通过回调函数处理DTMF信号,并提供了实例代码帮助理解。
PJSIP源码比较强大,先说一下DTMF是什么?

双音多频 DTMF(Dual Tone Multi Frequency),双音多频,由高频群和低频群组成,高低频群各包含4个频率。一个高频信号和一个低频信号叠加组成一个组合信号,代表一个数字。DTMF信号有16个编码。利用DTMF信令可选择呼叫相应的对讲机   
双音多频信号(DTMF),电话系统中电话机与交换机之间的一种用户信令,通常用于发送被叫号码。   在使用双音多频信号之前,电话系统中使用一连串的断续脉冲来传送被叫号码,称为脉冲拨号。脉冲拨号需要电信局中的操作员手工完成长途接续(早期方法,很老很古董)。   双音多频信号是贝尔实验室发明的,其目的是为了自动完成长途呼叫。   
双音多频的拨号键盘是4×4的矩阵,每一行代表一个低频,每一列代表一个高频。每按一个键就发送一个高频和低频的正弦信号组合,比如'1'相当于697和1209赫兹(Hz)。交换机可以解码这些频率组合并确定所对应的按键。

通过调用回调函数就可以达到目标
static void on_dtmf_digit(pjsua_call_id call_id, int digit)

在PJSIP例子中:
pjsip-apps\src\pjsua\pjsua_app.c(5418):    app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;

实现部分:

/*
* DTMF callback.
*/
static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
{
    PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
}

转至:http://www.zhimax.com/article/vc/on_dtmf_digit.html


pj_status_t pjsua_media_channel_create_sdp_t38(pjsua_call_id call_id, pj_pool_t *pool, const pjmedia_sdp_session *rem_sdp, pjmedia_sdp_session **p_sdp, int *sip_status_code, pj_bool_t createFwRule) { enum { MAX_MEDIA = 1 }; pjmedia_sdp_session *sdp; pjmedia_transport_info tpinfo; pjsua_call *call = &pjsua_var.calls[call_id]; pj_status_t status; int i; # ifdef SUPPORT_METABOLIC_MEDIA_PORT pj_uint16_t medport; pj_bool_t portAvaible = PJ_FALSE; # endif #if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 char localIp[CMSIP_STR_40]; pj_uint16_t localPort; pj_uint16_t localRtcpPort; pj_str_t ip; PJ_FIREWALL_RULE* pFwRule = NULL; #endif pj_str_t *pBoundIp = &pjsua_var.BoundIp; int family = AF_INET; /* Return error if media transport has not been created yet * (e.g. application is starting) */ if (call->med_tp == NULL) { return PJ_EBUSY; } /* Media index must have been determined before */ pj_assert(call->audio_idx != -1); /* Create media if it's not created. This could happen when call is * currently on-hold */ if (call->med_tp_st == PJSUA_MED_TP_IDLE) { pjsip_role_e role; role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC); status = pjsua_media_channel_init(call_id, role, call->secure_level, pool, rem_sdp, sip_status_code); if (status != PJ_SUCCESS) { return status; } } /* Get media socket info */ pjmedia_transport_info_init(&tpinfo); # ifdef SUPPORT_METABOLIC_MEDIA_PORT #ifdef INCLUDE_VOIP_SUPPORT_IPV6 if (pjsua_var.BoundIp6.slen <= 0 || !strncmp(pjsua_var.BoundIp6.ptr, "::", 2)) { CMSIP_PRINT("-------use IPv4--------"); family = pj_AF_INET(); pBoundIp = &pjsua_var.BoundIp; } else { CMSIP_PRINT("-------use IPv6--------"); family = pj_AF_INET6(); pBoundIp = &pjsua_var.BoundIp6; } #endif for (i = 0; i < PJMEDIA_PORT_TEST_COUNT; ++i) { medport = pjmedia_endpt_get_mediaPort(pjsua_var.med_endpt); pjmedia_endpt_set_mediaPort(pjsua_var.med_endpt); /*test RTP media port*/ status = pjmedia_endpt_test_media_port(*pBoundIp, medport, &portAvaible, family); if (PJ_FALSE==portAvaible && EADDRINUSE == errno) { CMSIP_PRINT("-------address is already used-------"); errno = 0; continue; } else if (PJ_SUCCESS != status) { CMSIP_PRINT("----------test media port error-----------"); return status; } /*test RTCP media port*/ status = pjmedia_endpt_test_media_port(*pBoundIp, (medport+1), &portAvaible, family); if (PJ_FALSE==portAvaible && EADDRINUSE == errno) { CMSIP_PRINT("-------address is already used-------"); errno = 0; continue; } else if (PJ_SUCCESS != status) { CMSIP_PRINT("----------test media port error-----------"); return status; } else { break; } } if (i >= PJMEDIA_PORT_TEST_COUNT) { return status; } pjmedia_transport_udp_set_mediaPort(call->med_tp, medport); # endif pjmedia_transport_get_info(call->med_tp, &tpinfo); #if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 if (createFwRule) { for (i = 0; i < PJ_ARRAY_SIZE(call->fwRule); ++i) { CMSIP_PRINT("--------ready to delete iptables rule--------"); pFwRule = &call->fwRule[i]; if (pFwRule->destination.slen > 0 && pFwRule->dport > 0) { pj_firewall_set_rule_accept(PJ_FIREWALLCFG_DEL, PJ_TRANSPORT_UDP, &pFwRule->destination, NULL, pFwRule->dport, NULL, NULL, -1, pj_firewall_get_fwtype(pFwRule->family) #ifdef INCLUDE_VOIP_SUPPORT_IPV6 , pFwRule->family #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ , pjsua_var.tcMark); } } pjmedia_transport_udp_local_info(call->med_orig, localIp, &localPort, &localRtcpPort, &family); ip = pj_str(localIp); CMSIP_PRINT("-----add iptables rule(dstIp(%s), localPort(%d), localRtcpPort(%d))", localIp, localPort, localRtcpPort); /*����DMZ������ʱ������Է���rtp�ȵ����ʹ�ñ�������DUT VOIP��rtp���ݰ�֮��ֱ�� ��DMZ��iptables����ת��������DUT VOIP�޷����յ����ݰ�������������Ҫ�ڷ���INVITE֮ ǰ�Ϳ�����Ӧ�˿ڡ�by yuchuwei*/ /*now, only support UDP*/ #ifdef INCLUDE_VOIP_SUPPORT_IPV6 if (AF_INET == family) { #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ status = pj_firewall_set_rule_accept(PJ_FIREWALLCFG_ADD, PJ_TRANSPORT_UDP, &ip, NULL, localPort, NULL, NULL, -1, pjsua_var.fwType #ifdef INCLUDE_VOIP_SUPPORT_IPV6 , AF_INET #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ , pjsua_var.tcMark); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error create Netfilter rule", status); return status; } #ifdef INCLUDE_VOIP_SUPPORT_IPV6 } #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ CMSIP_PRINT("=============add iptables rule end===================================="); pFwRule = &call->fwRule[0]; memset(pFwRule, 0, sizeof(PJ_FIREWALL_RULE)); pFwRule->protocol = PJ_TRANSPORT_UDP; strcpy(pFwRule->dstBuf, localIp); pFwRule->destination = pj_str(pFwRule->dstBuf); pFwRule->dport = localPort; pFwRule->sport = -1; pFwRule->family = family; status = pj_firewall_set_rule_accept(PJ_FIREWALLCFG_ADD, PJ_TRANSPORT_UDP, &ip, NULL, localRtcpPort, NULL, NULL, -1, pj_firewall_get_fwtype(family) #ifdef INCLUDE_VOIP_SUPPORT_IPV6 , family #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ , pjsua_var.tcMark); if (status != PJ_SUCCESS) { #ifdef INCLUDE_VOIP_SUPPORT_IPV6 if (AF_INET == family) { #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ pj_firewall_set_rule_accept(PJ_FIREWALLCFG_DEL, PJ_TRANSPORT_UDP, &ip, NULL, localPort, NULL, NULL, -1, pjsua_var.fwType #ifdef INCLUDE_VOIP_SUPPORT_IPV6 , pj_AF_INET() #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ , pjsua_var.tcMark); #ifdef INCLUDE_VOIP_SUPPORT_IPV6 } #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ pjsua_perror(THIS_FILE, "Error create Netfilter rule", status); return status; } CMSIP_PRINT("=============add iptables rule end===================================="); pFwRule = &call->fwRule[1]; memset(pFwRule, 0, sizeof(PJ_FIREWALL_RULE)); pFwRule->protocol = PJ_TRANSPORT_UDP; strcpy(pFwRule->dstBuf, localIp); pFwRule->destination = pj_str(pFwRule->dstBuf); pFwRule->dport = localRtcpPort; pFwRule->sport = -1; pFwRule->family = family; } #endif /* Create SDP */ status = pjmedia_endpt_create_sdp_t38(pjsua_var.med_endpt, pool, MAX_MEDIA, &tpinfo.sock_info, &sdp); if (status != PJ_SUCCESS) { if (sip_status_code) { *sip_status_code = 500; } return status; } /* If we're answering and the selected media is not the first media * in SDP, then fill in the unselected media with with zero port. * Otherwise we'll crash in transport_encode_sdp() because the media * lines are not aligned between offer and answer. */ if (rem_sdp && call->audio_idx != 0) { unsigned i; for (i=0; i<rem_sdp->media_count; ++i) { const pjmedia_sdp_media *rem_m = rem_sdp->media[i]; pjmedia_sdp_media *m; const pjmedia_sdp_attr *a; if ((int)i == call->audio_idx) continue; m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); pj_strdup(pool, &m->desc.media, &rem_m->desc.media); pj_strdup(pool, &m->desc.transport, &rem_m->desc.transport); m->desc.port = 0; /* Add one format, copy from the offer. And copy the corresponding * rtpmap and fmtp attributes too. */ m->desc.fmt_count = 1; pj_strdup(pool, &m->desc.fmt[0], &rem_m->desc.fmt[0]); if ((a=pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtpmap", &m->desc.fmt[0])) != NULL) { m->attr[m->attr_count++] = pjmedia_sdp_attr_clone(pool, a); } if ((a=pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "fmtp", &m->desc.fmt[0])) != NULL) { m->attr[m->attr_count++] = pjmedia_sdp_attr_clone(pool, a); } if (i==sdp->media_count) sdp->media[sdp->media_count++] = m; else { pj_array_insert(sdp->media, sizeof(sdp->media[0]), sdp->media_count, i, &m); ++sdp->media_count; } } } #if SUPPORT_STUN /* Add NAT info in the SDP */ if (pjsua_var.ua_cfg.nat_type_in_sdp) { pjmedia_sdp_attr *a; pj_str_t value; char nat_info[80]; value.ptr = nat_info; if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) { value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info), "%d", pjsua_var.nat_type); } else { const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type); value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info), "%d %s", pjsua_var.nat_type, type_name); } a = pjmedia_sdp_attr_create(pool, "X-nat", &value); pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a); } #endif /* SUPPORT_STUN */ /* Give the SDP to media transport */ /* pjsip will crash if the result of negotiation is T38 and this media is the peer's second media */ #if 0 status = pjmedia_transport_encode_sdp(call->med_tp, pool, sdp, rem_sdp, call->audio_idx); #else if (rem_sdp) { status = pjmedia_transport_encode_sdp(call->med_tp, pool, sdp, rem_sdp, call->audio_idx); } else { status = pjmedia_transport_encode_sdp(call->med_tp, pool, sdp, rem_sdp, 0); } #endif if (status != PJ_SUCCESS) { if (sip_status_code) *sip_status_code = PJSIP_SC_NOT_ACCEPTABLE; return status; } /* Update currently advertised RTP source address */ pj_memcpy(&call->med_rtp_addr, &tpinfo.sock_info.rtp_addr_name, sizeof(pj_sockaddr)); *p_sdp = sdp; return PJ_SUCCESS; } static void stop_media_session(pjsua_call_id call_id) { CMSIP_PRINT("--Now, stop media session-------"); /*ycw-pjsip:here*/ pjsua_call *call = &pjsua_var.calls[call_id]; /*ycw-pjsip-delete conference*/ #if 0 if (call->conf_slot != PJSUA_INVALID_ID) { if (pjsua_var.mconf) { pjsua_conf_remove_port(call->conf_slot); } call->conf_slot = PJSUA_INVALID_ID; } #endif /*ycw-pjsip*/ PJSUA_LOCK(); if (call->session) { /*ycw-pjsip.20111104*/ #if 0 pjmedia_rtcp_stat stat; if ((call->media_dir & PJMEDIA_DIR_ENCODING) #if 0 && (pjmedia_session_get_stream_stat(call->session, 0, &stat) == PJ_SUCCESS) #endif ) { /* Save RTP timestamp & sequence, so when media session is * restarted, those values will be restored as the initial * RTP timestamp & sequence of the new media session. So in * the same call session, RTP timestamp and sequence are * guaranteed to be contigue. */ #if 0 call->rtp_tx_seq_ts_set = 1 | (1 << 1); call->rtp_tx_seq = stat.rtp_tx_last_seq; call->rtp_tx_ts = stat.rtp_tx_last_ts; #endif } #endif if (pjsua_var.ua_cfg.cb.on_stream_destroyed) { pjsua_var.ua_cfg.cb.on_stream_destroyed(call_id, call->session, 0); } pjmedia_session_destroy(call->session, call_id, call->cmHangup #if defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP #if defined(INCLUDE_USB_VOICEMAIL) , call->isUsbVm #endif #endif # if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 #ifdef INCLUDE_VOIP_SUPPORT_IPV6 ,pj_firewall_get_fwtype(call->fwRule[0].family) #else ,pjsua_var.fwType #endif , pjsua_var.tcMark #endif ); call->session = NULL; PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", call_id)); } call->media_st = PJSUA_CALL_MEDIA_NONE; /*ycw-pjsip*/ PJSUA_UNLOCK(); CMSIP_PRINT("---stop media session. media state is none"); } pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) { pjsua_call *call = &pjsua_var.calls[call_id]; stop_media_session(call_id); # if defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP if ( # if defined(INCLUDE_USB_VOICEMAIL) call->isUsbVm == PJ_TRUE # else 0 # endif || # if defined(INCLUDE_PSTN_GATEWAY) call->isPstn # else 0 # endif ) { # if defined(INCLUDE_USB_VOICEMAIL) || defined(INCLUDE_PSTN_GATEWAY) pjsua_media_transport_destroy_for_single_call(call_id); # endif } # endif if (call->med_tp_st != PJSUA_MED_TP_IDLE) { pjmedia_transport_media_stop(call->med_tp); call->med_tp_st = PJSUA_MED_TP_IDLE; } if (call->med_orig && call->med_tp && call->med_tp != call->med_orig) { pjmedia_transport_close(call->med_tp); call->med_tp = call->med_orig; } /*ycw-pjsip-delete sound device*/ #if 0 check_snd_dev_idle(); #endif # if (defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP) && \ (defined(INCLUDE_PSTN_GATEWAY) || defined(INCLUDE_USB_VOICEMAIL)) call->medTpReady = PJ_FALSE; # endif return PJ_SUCCESS; } # if 0 /* * DTMF callback from the stream. */ static void dtmf_callback(pjmedia_stream *strm, void *user_data, int digit) { PJ_UNUSED_ARG(strm); /* For discussions about call mutex protection related to this * callback, please see ticket #460: * http://trac.pjsip.org/repos/ticket/460#comment:4 */ if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { pjsua_call_id call_id; call_id = (pjsua_call_id)(long)user_data; pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); } } # endif pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) { pjsua_call *call = &pjsua_var.calls[call_id]; pjmedia_session_info sess_info; pjmedia_stream_info *si = NULL; pj_status_t status; unsigned i; #if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 PJ_FIREWALL_RULE* pFwRule = NULL; #endif char localIp[CMSIP_STR_40] = {0}; pj_uint16_t rtpPort = 0; pj_uint16_t rtcpPort = 0; pj_str_t tmpStr; int family = AF_INET; if (!pjsua_var.med_endpt) { /* We're being shutdown */ return PJ_EBUSY; } /*ycw-pjsip*/ /*we must compare the new stream with the old stram to decide to destroy the old stream or not*/ /* Destroy existing media session, if any. */ #if 0 prev_media_st = call->media_st; stop_media_session(call->index); #endif memset(&sess_info, 0, sizeof(sess_info)); /* Create media session info based on SDP parameters. */ status = pjmedia_session_info_from_sdp( call->inv->pool_prov, pjsua_var.med_endpt, PJMEDIA_MAX_SDP_MEDIA, &sess_info, local_sdp, remote_sdp); if (status != PJ_SUCCESS) { return status; } for (i = 0; i < sess_info.stream_cnt; ++i) { if (sess_info.stream_info[i].dir == PJMEDIA_DIR_NONE) { continue; } if (sess_info.stream_info[i].ulptime== 0) { sess_info.stream_info[i].ulptime = 20; } } /* Update audio index from the negotiated SDP */ call->audio_idx = find_audio_index(local_sdp, PJ_TRUE); CMSIP_PRINT("---call audio index(%d)---", call->audio_idx); /* Find which session is audio */ PJ_ASSERT_RETURN(call->audio_idx != -1, PJ_EBUG); PJ_ASSERT_RETURN(call->audio_idx < (int)sess_info.stream_cnt, PJ_EBUG); si = &sess_info.stream_info[call->audio_idx]; /* Reset session info with only one media stream */ sess_info.stream_cnt = 1; CMSIP_PRINT("-----stream cnt(%d)---", sess_info.stream_cnt); /*ycw-pjsip-t38. Ŀǰֻ֧��һ����*/ if (si != &sess_info.stream_info[0]) { pj_memcpy(&sess_info.stream_info[0], si, sizeof(pjmedia_stream_info)); si = &sess_info.stream_info[0]; } pjmedia_transport_udp_local_info(call->med_orig, localIp, &rtpPort, &rtcpPort, &family); CMSIP_PRINT("Get local rtp network address:%s:%d", localIp, rtpPort); CMSIP_PRINT("Get local rtcp network address:%s:%d", localIp, rtcpPort); CMSIP_PRINT("Get local family:%d(AF_INET:%d,AF_INET6:%d)", family, AF_INET, AF_INET6); tmpStr = pj_str(localIp); pj_sockaddr_init(family, &si->local_addr, &tmpStr, rtpPort); pj_sockaddr_init(family, &si->local_rtcp, &tmpStr, rtcpPort); /*ycw-pjsip-t38*/ /*for the new re-INVITE, we accept all. But, we must check the new stream equal the old stream or not. If they are same, we need not re-create the stream*/ if (call->session && call->session->stream_cnt == sess_info.stream_cnt && pjsua_media_stream_info_equal(&call->session->stream_info[0], si) /*now, just support one stream */) { CMSIP_PRINT("----the existed session has the same transport info, do not re-create--"); goto ON_SUCCESS; } CMSIP_PRINT("destroy old stream, create new stream."); /*ycw-pjsip*/ stop_media_session(call->index); /* Check if no media is active */ if (sess_info.stream_cnt == 0 || si->dir == PJMEDIA_DIR_NONE) { /* Call media state */ call->media_st = PJSUA_CALL_MEDIA_NONE; /* Call media direction */ call->media_dir = PJMEDIA_DIR_NONE; /* Don't stop transport because we need to transmit keep-alives, and * also to prevent restarting ICE negotiation. See * http://trac.pjsip.org/repos/ticket/1094 */ #if 0 /* Shutdown transport's session */ pjmedia_transport_media_stop(call->med_tp); call->med_tp_st = PJSUA_MED_TP_IDLE; /* No need because we need keepalive? */ /* Close upper entry of transport stack */ if (call->med_orig && (call->med_tp != call->med_orig)) { pjmedia_transport_close(call->med_tp); call->med_tp = call->med_orig; } #endif } else { pjmedia_transport_info tp_info; /* Start/restart media transport */ CMSIP_PRINT("----start media transport-------------"); status = pjmedia_transport_media_start(call->med_tp, call->inv->pool_prov, local_sdp, remote_sdp, call->audio_idx); if (status != PJ_SUCCESS) return status; CMSIP_PRINT("-----media transport state is running---------"); call->med_tp_st = PJSUA_MED_TP_RUNNING; /* Get remote SRTP usage policy */ pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call->med_tp, &tp_info); if (tp_info.specific_info_cnt > 0) { unsigned i; for (i = 0; i < tp_info.specific_info_cnt; ++i) { if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; call->rem_srtp_use = srtp_info->peer_use; break; } } } /*ycw-pjsip*/ #if 0 /* Override ptime, if this option is specified. */ if (pjsua_var.media_cfg.ptime != 0) { si->param->setting.frm_per_pkt = (pj_uint8_t) (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); if (si->param->setting.frm_per_pkt == 0) si->param->setting.frm_per_pkt = 1; } #endif /* Disable VAD, if this option is specified. */ if (pjsua_var.media_cfg.no_vad) { si->param->setting.vad = 0; } /* Optionally, application may modify other stream settings here * (such as jitter buffer parameters, codec ptime, etc.) */ #if 0 si->jb_init = pjsua_var.media_cfg.jb_init; si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre; si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre; si->jb_max = pjsua_var.media_cfg.jb_max; #endif /* ycw-pjsip: vbd support */ if (call->request_channel_mode == pj_channel_passthrough) { #if 0 si->jb_min_pre = si->jb_max_pre = -2; #endif si->vad = si->cng = 0; } /* Set SSRC */ #if 0 si->ssrc = call->ssrc; #endif /* Set RTP timestamp & sequence, normally these value are intialized * automatically when stream session created, but for some cases (e.g: * call reinvite, call update) timestamp and sequence need to be kept * contigue. */ #if 0 si->rtp_ts = call->rtp_tx_ts; si->rtp_seq = call->rtp_tx_seq; si->rtp_seq_ts_set = call->rtp_tx_seq_ts_set; #endif #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Enable/disable stream keep-alive and NAT hole punch. */ si->use_ka = (call->acc_id >= 0) ? pjsua_var.acc[call->acc_id].cfg.use_stream_ka : PJ_TRUE; #endif /* Create session based on session info. */ status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, &call->med_orig, call, call_id, # if (!(defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP) || \ defined(INCLUDE_PSTN_GATEWAY)) &call->dsp_med_tp, # endif # if defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP # if defined(INCLUDE_USB_VOICEMAIL) call->isUsbVm, # endif # if defined(INCLUDE_PSTN_GATEWAY) call->isPstn, # endif # endif # if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 #ifdef INCLUDE_VOIP_SUPPORT_IPV6 pj_firewall_get_fwtype(call->fwRule[0].family), #else pjsua_var.fwType, #endif pjsua_var.tcMark, # endif &call->session ); if (status != PJ_SUCCESS) { return status; } #if defined(PJSIP_HAS_FIREWALL_FILTER) && PJSIP_HAS_FIREWALL_FILTER!=0 for (i = 0; i < PJ_ARRAY_SIZE(call->fwRule); ++i) { pFwRule = &call->fwRule[i]; if (pFwRule->destination.slen > 0 && pFwRule->dport > 0) { status = pj_firewall_set_rule_accept(PJ_FIREWALLCFG_DEL, pFwRule->protocol, &(pFwRule->destination), NULL, pFwRule->dport, NULL, NULL, -1, pj_firewall_get_fwtype(pFwRule->family) #ifdef INCLUDE_VOIP_SUPPORT_IPV6 , pFwRule->family #endif /* INCLUDE_VOIP_SUPPORT_IPV6 */ , pjsua_var.tcMark); if (status != PJ_SUCCESS) { printf("==%s,%d==firewall rule set error!!!!====", __FUNCTION__, __LINE__); } } memset(pFwRule, 0, sizeof(PJ_FIREWALL_RULE)); } #endif CMSIP_PRINT("----session create successfully--------------------------------"); /* If DTMF callback is installed by application, install our * callback to the session. */ if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { #if 0 pjmedia_session_set_dtmf_callback(call->session, 0, &dtmf_callback, (void*)(long)(call->index)); #endif } /* Call media direction */ call->media_dir = si->dir; /* Call media state */ if (call->local_hold) { call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; } else if (call->media_dir == PJMEDIA_DIR_DECODING) { call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; } else { call->media_st = PJSUA_CALL_MEDIA_ACTIVE; } } ON_SUCCESS: /* Print info. */ { char info[80]; int info_len = 0; unsigned i; for (i = 0; i < sess_info.stream_cnt; ++i) { int len; const char *dir; pjmedia_stream_info *strm_info = &sess_info.stream_info[i]; switch (strm_info->dir) { case PJMEDIA_DIR_NONE: dir = "inactive"; break; case PJMEDIA_DIR_ENCODING: dir = "sendonly"; break; case PJMEDIA_DIR_DECODING: dir = "recvonly"; break; case PJMEDIA_DIR_ENCODING_DECODING: dir = "sendrecv"; break; default: dir = "unknown"; break; } len = pj_ansi_sprintf( info+info_len, ", stream #%d: %.*s (%s)", i, (int)strm_info->fmt.encoding_name.slen, strm_info->fmt.encoding_name.ptr, dir); if (len > 0) { info_len += len; } } PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); } return PJ_SUCCESS; } 在这段代码插入我的一个需求,也就是通知内核态接下来rtp的端口你觉得适合吗
最新发布
10-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值