
缩写注释:
SDP(会话描述协议): 用于两个会话实体之间的媒体协商,并达成一致,属信令语言族,采用文本(字符)描述形式;
RTP (实时传输协议):由RFC3550定义的端到端的传输实时数据协议,它包含:净荷类型识别,序列号编码,时间戳和传输控制;
RTCP(实时传输控制协议):与RTP共同定义在1996年提出的RFC 1889中,是和 RTP一起工作的控制协议。
RSVP(资源预留协议):RSVP是一种位于第三层的信令协定,它独立于各种网路媒介,使得套用能将自己的QoS要求通过信令通知给网路,网路可以对此套用预留相应的资源。
Sigcomp(信令压缩):一种压缩应用层协议(如SIP、RTSP)消息的方案。

SIP标准:
核心标准:RFC3261 SIP:Session Initiation Protocol
扩展标准:RFC2976 The SIP INFO Method
RFC3263 Locating SIP Servers
RFC3265 SIP-Specific Event Notification
RFC3311 UPDATE Method
RFC3326 The Reason Header Field
RFC3372 SIP for Telephones (SIP-T): Context and Architectures
RFC3398 ISUP to SIP Mapping
RFC3428 SIP Extension for Instant Messaging

1,初始化PJSUA
status = pjsua_create();
if (status != PJ_SUCCESS) { printf("Error in pjsua_create(%d)\n", status); }
pjsua_config_default(&(ua_cfg));
ua_cfg.cb.on_incoming_call = &on_incoming_call; (接听电话回调)
ua_cfg.cb.on_call_media_state = &on_call_media_state; (媒体事件回调)
ua_cfg.cb.on_call_state = &on_call_state; (播出电话的回调)
ua_cfg.cb.on_pager = &on_pager; (进来message的回调)
ua_cfg.cb.on_pager_status = &on_pager_status; (短信发送的状态变化)
ua_cfg.cb.on_typing = &on_typing; /* configure logging */ (对方输入消息的动作回调)
pjsua_logging_config_default(&(log_cfg));
log_cfg.console_level = 4; /* configure media */
pjsua_media_config_default(&(media_cfg));
status = pjsua_init(&ua_cfg, &log_cfg, NULL);
if (status != PJ_SUCCESS) { printf("Error in pjsua_init(%d)\n", status); }
pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
pool = pjsua_pool_create("MYSOUNDPOOL", 4000, 4000);
// pj_pool_t *pcmpool = pjsua_pool_create("PCMPOOL", 2000, 2000); /* Add UDP transport. */ { pjsua_transport_config_default(&trans_cfg);
trans_cfg.port = 5060;
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &trans_cfg, &transport_id);
if (status != PJ_SUCCESS) { printf("Error creating transport(%d)\n", status); } }
pjsua_set_null_snd_dev(); /* Initialization is done, now start pjsua */
status = pjsua_start();
if (status != PJ_SUCCESS) { printf("Error starting pjsua(%d)\n", status); } /* Register to SIP server by creating SIP account. */ {
pjsua_acc_config_default(&acc_cfg);
acc_cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
acc_cfg.reg_uri = pj_str("sip:" SIP_DOMAIN);
acc_cfg.cred_count = 1;
acc_cfg.cred_info[0].realm = pj_str(SIP_DOMAIN);
acc_cfg.cred_info[0].scheme = pj_str("digest");
acc_cfg.cred_info[0].username = pj_str(SIP_USER);
acc_cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
// acc_cfg.cred_info[0].data = pj_str(SIP_PASSWD);
status = pjsua_acc_add_local(transport_id, PJ_TRUE, &acc_id);
if (status != PJ_SUCCESS) { printf("Error adding account(%d)\n", status); }
}
2,拨号
pj_str_t to = pj_str(toaddr);
status = pjsua_call_make_call(acc_id, &to, NULL, NULL, NULL, ¤t_call);
3,建立语音通道(对端接听通话后on_call_media_state会触发,media状态为PJSUA_CALL_MEDIA_ACTIVE)
static void on_call_media_state(pjsua_call_id call_id){
pjsua_call_info ci;
pjsua_call_get_info(call_id, &ci);
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
pjsua_conf_get_port_info(ci.conf_slot, &cpi);
//初始化gaudo为自定义的音频设备
gaudo->setupmodev(cpi.bits_per_sample);
gaudo->setupmidev(cpi.bits_per_sample);
// create pcm micro media port
pjmedia_port *pcmport;
port_data *sine;
unsigned i;
unsigned count;
unsigned samplerate = cpi.clock_rate;
unsigned channel_count = 1;
//创建麦克风的输入port
pcmport = (pjmedia_port *)pj_pool_zalloc(pool, sizeof(pjmedia_port));
pj_str_t name = pj_str("PCMAUDPORT");
pjmedia_port_info_init(&pcmport->info, &name, PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'), samplerate, channel_count, 16, samplerate * 62.5 / 1000 * channel_count);
pcmport->get_frame = &sine_get_frame; //从麦克风读数据,写给输出音频
//创建喇叭的输出port
pjmedia_port *prt;
pjsua_conf_port_id port_id, pcmport_id;
prt = (pjmedia_port *)pj_pool_zalloc(pool, sizeof(pjmedia_port));
name = pj_str("PCMAUDPORT2");
pjmedia_port_info_init(&prt->info, &name, PJMEDIA_SIG_CLASS_PORT_AUD('s', 'o'), samplerate, channel_count, 16, samplerate * 62.5 / 1000 * channel_count);
prt->put_frame = &sine_put_frame; //从输入音频流读数据,写给喇叭
pjsua_conf_add_port(pool, prt, &port_id);
pjsua_conf_add_port(pool, pcmport, &pcmport_id);
gport_id = port_id ;
gport = prt;
gpcmport_id = pcmport_id;
gpcmport = pcmport;
PJ_LOG(3, (THIS_FILE, "Media state conf_slot[%d] pcmport_id[%d]!!\n", ci.conf_slot, pcmport_id));
//麦克风的输入数据传输给通话对端
pjsua_conf_connect(pcmport_id, ci.conf_slot);
//通话对端传输的数据传输给喇叭播放
pjsua_conf_connect(ci.conf_slot, port_id);
}}
4,本地音频处理
static pj_status_t sine_put_frame(struct pjmedia_port *port, pjmedia_frame *frame){
MI_S32 s32Ret = MI_SUCCESS; MI_AUDIO_DEV AoDevId = 0;
MI_AO_CHN AoChn = 0; MI_AUDIO_Frame_t stAoSendFrame;
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
if (frame->size <= 0) { return PJ_SUCCESS;
}
memset(&stAoSendFrame, 0x0, sizeof(MI_AUDIO_Frame_t));
stAoSendFrame.u32Len = frame->size / PJMEDIA_PIA_CCNT(&port->info);
stAoSendFrame.apVirAddr[0] = frame->buf;
stAoSendFrame.apVirAddr[1] = NULL;
do {
//-1 blockmode, 0 nonblockmode
s32Ret = MI_AO_SendFrame(AoDevId, AoChn, &stAoSendFrame, -1);
} while (s32Ret == MI_AO_ERR_NOBUF);
if (s32Ret != MI_SUCCESS) { printf("[Warning]: MI_AO_SendFrame fail, error is 0x%x: \n", s32Ret); }
}
return PJ_SUCCESS;
}
static pj_status_t sine_get_frame(pjmedia_port *port, pjmedia_frame *frame){
void *samples = frame->buf;
unsigned i, left, right;
if (frame->size <= 0) { return PJ_SUCCESS; }
MI_AUDIO_Frame_t stAiChFrame;
MI_AUDIO_AecFrame_t stAecFrame;
MI_S32 s32Ret;
MI_U32 u32ChnIndex;
struct timeval tv_before, tv_after;
MI_S64 before_us, after_us;
MI_AI_AedResult_t stAedResult; MI_S32 s32Doa; MI_U32 AiDevId = 0;
MI_U32 AiChn = 0; MI_BOOL bEnableAed = false;
memset(&stAiChFrame, 0, sizeof(MI_AUDIO_Frame_t));
memset(&stAecFrame, 0, sizeof(MI_AUDIO_AecFrame_t)); //-1 blockmode, 0 nonblockmode
s32Ret = MI_AI_GetFrame(AiDevId, AiChn, &stAiChFrame, &stAecFrame, -1);
if (MI_SUCCESS == s32Ret) {
memcpy(samples, stAiChFrame.apVirAddr[0], stAiChFrame.u32Len);
MI_AI_ReleaseFrame(AiDevId, AiChn, &stAiChFrame, NULL);
}
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
return PJ_SUCCESS;
}
5,挂断
status = pjsua_call_hangup(current_call, 0, NULL, NULL);
参考链接:
https://www.renrendoc.com/paper/110170462.html
网友评论