用osip2+eXosip2+ortp+mediastreamer实现的linux简易软电话源代码(可实现通话)

编译命令: 
gcc -losip2 -leXosip2 -lmediastreamer -lpthread -o my_exosip_phone my_exosip_phone.c

my_exosip_phone.c如下: 
#include <eXosip2/eXosip.h> 
#include <osip2/osip_mt.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <mediastreamer2/mediastream.h> 
#include <pthread.h>


#define WAIT_TIMER 200 
#define REG_TIMER 30*1000 

int doing; 
int rtp_port; 
int dtmfing, calling,picked; 
int call_id ,dialog_id; 

char *dtmf_str="this is dtmf"; 
//CString dtmf_str; 
//char *server_url="192.168.18.249"; 
char *server_url="192.168.30.130"; 
char *server_port="5060"; 
char *local_port="5000"; 
char *username="2001"; 
char *password="2001"; 
char *telNum="2002"; 

AudioStream *audio = NULL; 
RtpProfile *profile = NULL; 
RtpSession *session = NULL; 
OrtpEvQueue *q = NULL;


int build_media(int local_port, const char *remote_ip, int remote_port, int payload, const char *fmtp, int jitter, int ec, int bitrate) 

if(payload != 0 && payload != 8) 

/* 目前仅支持0,8 711ulaw,711alaw */ 
return -1; 


PayloadType *pt; 

profile = rtp_profile_clone_full(&av_profile); 
q = ortp_ev_queue_new(); 

pt = rtp_profile_get_payload(profile, payload); 
if (pt==NULL) 

printf("codec error!"); 
return -1; 


if (fmtp != NULL) payload_type_set_send_fmtp(pt, fmtp); 
if (bitrate > 0) pt->normal_bitrate = bitrate; 

if (pt->type != PAYLOAD_VIDEO) 

printf("audio stream start!"); 
audio = audio_stream_start(profile, local_port, remote_ip, remote_port, payload, jitter, ec); 
if (audio) 

session = audio->session; 

else 

printf("session error!"); 
return -1; 


else 

printf("codec select error!"); 
return -1; 


rtp_session_register_event_queue(session, q); 
return 0; 
}


int real_send_register(int expires) 

char identity[100]; 
char registerer[100]; 
char localip[128]; 
eXosip_guess_localip (AF_INET, localip, 128); 
sprintf(identity,"sip:%s@%s:%s",username,localip,local_port); 
sprintf(registerer,"sip:%s:%s",server_url,server_port); 

osip_message_t *reg = NULL; 

eXosip_lock (); 
int ret = eXosip_register_build_initial_register(identity, registerer, NULL,expires, &reg); 
eXosip_unlock (); 
if(0 > ret) 

printf("register init Failed!"); 
return -1; 


eXosip_lock (); 
eXosip_clear_authentication_info(); //去除上次加入的错误认证信息 
eXosip_add_authentication_info(username, username,password, "md5", NULL); 
ret = eXosip_register_send_register(ret, reg); 
eXosip_unlock (); 
if(0 != ret) 

printf("register send Failed!"); 
return -1; 


return 0; 
}


int sip_ua_monitor() 

int ret = -1; 
char *payload_str; /* 服务器优先编码值 */ 
char localip[128]; 
char tmp[4096]; 
char dtmf[50] = {0}; 

int reg_remain = REG_TIMER; 
usleep(500); 
printf("Event monitor for uac/uas start!/n"); 

eXosip_event_t *uac_e; /* 事件处理 */ 
osip_message_t *ack = NULL; /* 响应消息 */ 
osip_message_t *answer = NULL; /* 请求消息的回应 */ 

/* 响应SDP(用于UAC) */ 
sdp_message_t * msg_rsp = NULL; 
sdp_connection_t * con_rsp = NULL; 
sdp_media_t * md_rsp = NULL; 

/* 请求SDP(用于UAS) */ 
sdp_message_t * msg_req = NULL; 
sdp_connection_t * con_req = NULL; 
sdp_media_t * md_req = NULL; 

char out_str[100] = {0}; 

eXosip_lock (); 
eXosip_automatic_action(); 
eXosip_unlock (); 

while(doing) 

eXosip_lock (); 
uac_e = eXosip_event_wait (0, WAIT_TIMER); 
eXosip_unlock (); 

reg_remain = reg_remain - WAIT_TIMER; 
if(reg_remain < 0) 

//超时,重新注册 
eXosip_lock (); 
eXosip_automatic_refresh(); 
eXosip_unlock (); 
reg_remain = REG_TIMER; 
printf("register timeout,retry!"); 


if(dtmfing) 

strcpy(dtmf, dtmf_str); 
int index; 
for( index=0; index<50; index++) 

//依次读取字符 
if(dtmf[index] == '/0') break; 

/* 发送DTMF */ 
eXosip_lock(); 
audio_stream_send_dtmf(audio, dtmf[index]); 
eXosip_unlock(); 

sprintf(out_str, "DTMF send <%c> OK!", dtmf[index]); 
printf("%s",out_str); 
usleep(500); 


dtmfing = 0; 


if (uac_e == NULL) 

//DEBUG_INFO("nothing"); 
continue; 


eXosip_lock (); 
eXosip_default_action(uac_e); /* 处理407加入鉴权信息 */ 
eXosip_unlock (); 

if(NULL != uac_e->response) 

//UAC 消息处理前检查 
sprintf(out_str, "%d %s/n", uac_e->response->status_code, uac_e->response->reason_phrase); 
printf(out_str); 

if(487 == uac_e->response->status_code) 

printf("(取消呼叫成功)/n"); 
continue; 


if(480 == uac_e->response->status_code) 

//480 无应答 
printf("(无应答)/n");

picked = 0; 
calling = 0; 
call_id = 0; 
dialog_id = 0; 
continue; 

if(401 == uac_e->response->status_code) 

eXosip_clear_authentication_info(); //去除上次加入的错误认证信息 
eXosip_add_authentication_info(username, username,password, "md5", NULL); 
printf("register again!/n"); 



if(NULL != uac_e->request) 



if(NULL != uac_e->ack) 



switch (uac_e->type) 

case EXOSIP_CALL_SERVERFAILURE: 
case EXOSIP_CALL_RELEASED: 
/* 服务器错误或对方忙 */ 
printf("(对方或服务器正忙!)"); 

call_id = 0; 
dialog_id = 0; 
picked = 0; 
calling = 0; 

printf("Dest or Server Busy!"); 
break; 

/* UAS 处理事件 */ 
case EXOSIP_MESSAGE_NEW: //新的消息到来 
printf("EXOSIP_MESSAGE_NEW!/n"); 
if(MSG_IS_MESSAGE(uac_e->request)) //如果接受到的消息类型是MESSAGE 

osip_body_t *body; 
osip_message_get_body (uac_e->request, 0, &body); 
printf ("Reveivc a msg : %s/n", body->body); 

//按照规则,需要回复200 OK信息 
eXosip_message_build_answer (uac_e->tid, 200,&answer); 
eXosip_message_send_answer (uac_e->tid, 200,answer); 
break; 

case EXOSIP_CALL_INVITE: 
sprintf(out_str, "收到来自 %s 的呼叫!",uac_e->request->from->url->string); 
printf("%s/n",out_str); 

eXosip_lock (); 
eXosip_call_send_answer(uac_e->tid, 180, NULL); 
if(0 != eXosip_call_build_answer(uac_e->tid, 200, &answer)) 

eXosip_call_send_answer(uac_e->tid, 603, NULL); 
printf("error build answer!"); 
continue; 

eXosip_unlock (); 

call_id = uac_e->cid; //供挂断电话上下文操作 
dialog_id = uac_e->did; 

/* 构建本地SDP消息供媒体建立 */ 
eXosip_guess_localip(AF_INET, localip, 128); 
snprintf (tmp, 4096, 
"v=0/r/n" 
"o=youtoo 1 1 IN IP4 %s/r/n" 
"s=##youtoo demo/r/n" 
"c=IN IP4 %s/r/n" 
"t=0 0/r/n" 
"m=audio %d RTP/AVP 0 8 101/r/n" 
"a=rtpmap:0 PCMU/8000/r/n" 
"a=rtpmap:8 PCMA/8000/r/n" 
"a=rtpmap:101 telephone-event/8000/r/n" 
"a=fmtp:101 0-15/r/n", localip, localip, rtp_port); 

//设置回复的SDP消息体,下一步计划分析消息体 
eXosip_lock (); 
osip_message_set_body(answer, tmp, strlen(tmp)); 
osip_message_set_content_type(answer, "application/sdp"); 

/* 解析SDP */ 
msg_req = eXosip_get_remote_sdp(uac_e->did); 
con_req = eXosip_get_audio_connection(msg_req); 
md_req = eXosip_get_audio_media(msg_req); 
eXosip_unlock (); 

/* 
payload_str = (char *)osip_list_get(&md_req->m_payloads, 0); //取主叫媒体能力编码 
//暂时只支持0/8 711u/711a 
*/ 

calling = 1; 

while(!picked) 

//未接通进入循环检测 
usleep(200); 


eXosip_unlock (); 
eXosip_call_send_answer(uac_e->tid, 200, answer); 
eXosip_unlock (); 

printf("200 ok 发送"); 
break; 

case EXOSIP_CALL_CANCELLED: 
/* 拒绝接听 */ 

call_id = 0; 
dialog_id = 0; 
picked = 0; 
calling = 0; 
printf("被叫拒绝接听/n"); 
break; 

case EXOSIP_CALL_ACK: 
/* 返回200后收到ack才建立媒体 */ 
if(calling) 

/* 建立RTP连接 */ 
ret = build_media(rtp_port, con_req->c_addr, atoi(md_req->m_port), 0, NULL, 0, 0, 0); 
if(!ret) 

printf("媒体建立失败,无法创建通话,请挂断!"); 
//pMainWnd->OnHang(); 


break; 

/* UAC 处理事件 */ 
case EXOSIP_REGISTRATION_FAILURE: 
if(401 == uac_e->response->status_code) 

//4o1 Unauthorized register again 
printf("register again!/n"); 
/* 先清除鉴权信息,再应用新输入的鉴权信息 */ 
eXosip_clear_authentication_info(); 
eXosip_add_authentication_info(username, username, password, "md5", NULL);


osip_message_t *rereg; 
eXosip_lock (); 
eXosip_register_build_register(uac_e->rid, 300, &rereg);// 
//取回认证的字符串authorization 
//osip_authorization_t *auth; 
///char *strAuth; 
//osip_message_get_authorization((const osip_message_t *)rereg,0,&auth); 
//osip_authorization_to_str(auth,&strAuth); 
//strcpy(str_auth,strAuth);//保存认证字符串 
eXosip_register_send_register(uac_e->rid,rereg); 
eXosip_unlock (); 

break;

case EXOSIP_REGISTRATION_SUCCESS: 
printf("textinfo is %s/n", uac_e->textinfo); 
break; 

case EXOSIP_CALL_CLOSED: 
if(audio) 

//被动关闭媒体连接(远端触发) 
eXosip_lock (); 
audio_stream_stop(audio); 

ortp_ev_queue_destroy(q); 
rtp_profile_destroy(profile); 
eXosip_unlock (); 

audio = NULL; 
q = NULL; 
profile = NULL; 
printf("audio stream stoped!"); 


printf("(对方已挂断)"); 
call_id = 0; 
dialog_id = 0; 
picked = 0; 
calling = 0; 
break; 

case EXOSIP_CALL_PROCEEDING: 
printf("(查找连接中..)"); 
break; 

case EXOSIP_CALL_RINGING: 
printf("(对方振铃)"); 
call_id = uac_e->cid; 
dialog_id = uac_e->did; 

/* 
RingStream *r; 
MSSndCard *sc; 
sc=ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()); 
r=ring_start("D://mbstudio//vcwork//YouToo//dial.wav",2000,sc); 

Sleep(10); 
ring_stop(r); 
*/ 
break; 

case EXOSIP_CALL_ANSWERED: 
//ring_stop(ring_p); 
printf("(对方已接听)"); 

call_id = uac_e->cid; 
dialog_id = uac_e->did; 

eXosip_lock (); 
eXosip_call_build_ack (uac_e->did, &ack); 
eXosip_call_send_ack (uac_e->did, ack); 

/* 响应SIP消息中SDP分析 */ 
msg_rsp = eXosip_get_sdp_info(uac_e->response); 
con_rsp = eXosip_get_audio_connection(msg_rsp); 
md_rsp = eXosip_get_audio_media(msg_rsp); 

/* 取服务器支持的最优先的编码方式 */ 
payload_str = (char *)osip_list_get(&md_rsp->m_payloads, 0); 
eXosip_unlock (); 

/* 建立RTP连接 */ 
ret = build_media(rtp_port, con_rsp->c_addr, atoi(md_rsp->m_port), atoi(payload_str), NULL, 0, 0, 0); 
if(!ret) 

printf("媒体建立失败,无法创建通话,请挂断!"); 
//pMainWnd->OnHang(); 

break; 

default: 
break; 


eXosip_event_free (uac_e); 

fflush(stdout); 


return 0; 
}

void OnHang() 

int ret; 

if(audio) 

//主动关闭媒体连接(本地端) 
eXosip_lock (); 
audio_stream_stop(audio); 

ortp_ev_queue_destroy(q); 
rtp_profile_destroy(profile); 
eXosip_unlock (); 

printf("audio stream stoped!"); 
audio = NULL; 
q = NULL; 
profile = NULL; 


eXosip_lock (); 
ret = eXosip_call_terminate(call_id, dialog_id); 
eXosip_unlock (); 
if(0 != ret) 

printf("hangup/terminate Failed!/n"); 

else 

printf("(已挂断)"); 
call_id = 0; 
dialog_id = 0; 
picked = 0; 
calling = 0; 



int sip_init() 

int ret = 0; 

ret = eXosip_init (); 
eXosip_set_user_agent("##YouToo0.1"); 

if(0 != ret) 

printf("Couldn't initialize eXosip!/n"); 
return -1; 


ret = eXosip_listen_addr (IPPROTO_UDP, NULL, 5000, AF_INET, 0); 
if(0 != ret) 

eXosip_quit (); 
printf("Couldn't initialize transport layer!/n"); 
return -1; 


//AfxBeginThread(sip_ua_monitor,(void *)this); 
pthread_t id; 
int i=pthread_create(&id,NULL,(void *) sip_ua_monitor,NULL); 
if(i!=0) 

printf ("Create pthread error!/n"); 
return -1; 


/* rtp */ 
ortp_init(); 
//ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL); 

/* media */ 
ms_init(); 

return 0; 
}


int main() 

char *payload_str; 
char localip[128]; 
char tmp[4096]; 
char command; 
char dtmf[50] = {0}; 

int reg_remain = REG_TIMER; 
char out_str[100] = {0}; 

osip_message_t *invite=NULL; 
osip_message_t *info=NULL; 
osip_message_t *message=NULL; 

char source_call[100]; 
char dest_call[100]; 

doing = 1; /* 事件循环开关 */ 
calling = 0; /* 是否正在被叫(判断ack类型) */ 
picked = 0; 
dtmfing = 0; 

if(0 != sip_init ()) 

printf("Quit!/n"); 
return -1; 





rtp_port = 9900; //rtp 媒体本地端口 
//codec_id = 0; //编码 G711uLaw 0 

call_id = 0; 
dialog_id = 0; 



printf("r 向服务器注册/n/n"); 
printf("c 取消注册/n/n"); 
printf("i 发起呼叫请求/n/n"); 
printf("h 挂断/n/n"); 
printf("a 接听电话/n/n"); 
printf("q 推出程序/n/n"); 
printf("s 执行方法INFO/n/n"); 
printf("m 执行方法MESSAGE/n/n"); 

int flag=1; 

while(flag) 

//输入命令 
printf("Please input the command:/n"); 
scanf("%c",&command); 
getchar();

switch(command) 

case 'r':// 先清除鉴权信息,再应用新输入的鉴权信息 
real_send_register(1800); 
sleep(5); 
real_send_register(1800); 
break; 
case 'i': 

sprintf(source_call,"sip:%s@%s:%s",username,localip,local_port); 
sprintf(dest_call,"sip:%s@%s:%s",telNum,server_url,server_port); 

//char tmp[4096]; 
bzero(tmp,4096); 

int i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "This is a call invite"); 
if (i != 0) 

printf("Intial INVITE failed!/n"); 


char localip[128]; 

eXosip_guess_localip (AF_INET, localip, 128); 
snprintf (tmp, 4096, 
"v=0/r/n" 
"o=youtoo 1 1 IN IP4 %s/r/n" 
"s=##youtoo demo/r/n" 
"c=IN IP4 %s/r/n" 
"t=0 0/r/n" 
"m=audio %d RTP/AVP 0 8 101/r/n" 
"a=rtpmap:0 PCMU/8000/r/n" 
"a=rtpmap:8 PCMA/8000/r/n" 
"a=rtpmap:101 telephone-event/8000/r/n" 
"a=fmtp:101 0-15/r/n", localip, localip, rtp_port); 

osip_message_set_body (invite, tmp, strlen(tmp)); 
osip_message_set_content_type (invite, "application/sdp"); 

eXosip_lock (); 
i = eXosip_call_send_initial_invite (invite); 
eXosip_unlock (); 

break;

case 'h': //挂断 
OnHang(); 
break;

case 'c': 
real_send_register(0); 
//printf("This modal is not commpleted!/n"); 
break;

case 's': //传输INFO方法 
eXosip_call_build_info(dialog_id,&info); 
snprintf(tmp,4096,"/nThis is a sip message(Method:INFO)"); 
osip_message_set_body(info,tmp,strlen(tmp)); 
//格式可以任意设定,text/plain代表文本信息 
osip_message_set_content_type(info,"text/plain"); 
eXosip_call_send_request(dialog_id,info); 
break;

case 'm': 
//传输MESSAGE方法,也就是即时消息,和INFO方法相比,我认为主要区别是: 
//MESiSAGE不用建立连接,直接传输信息,而INFO消息必须在建立INVITE的基础上传输 
bzero(tmp,4096); 
printf("the method : MESSAGE/n"); 
eXosip_message_build_request(&message,"MESSAGE",dest_call,source_call,NULL); 
//内容,方法, to ,from ,route 
snprintf(tmp,4096,"This is a sip message(Method:MESSAGE)"); 
osip_message_set_body(message,tmp,strlen(tmp)); 
//假设格式是xml 
osip_message_set_content_type(message,"text/xml"); 
eXosip_message_send_request(message); 
break;

case 'q': 
doing = -1; 
flag=0; 
usleep(1000); //保证事件线程能退出(线程循环检测时间毫秒级<1000) 
//主动关闭媒体连接(本地端) 
printf("Bye!/n"); 
break; 

case 'a': 
picked = 1; 
printf("已接听/n"); 
break; 

case 'd': 
dtmfing=1; 
break; 
default: 
printf("input error! please input again/n"); 
break; 


return(0); 
}

你可能感兴趣的:(用osip2+eXosip2+ortp+mediastreamer实现的linux简易软电话源代码(可实现通话))