本文鏈接:https://blog.csdn.net/alwaysrun/article/details/103939541, 感謝作者。
PJSIP注冊呼叫流程簡介
注冊注銷
電話呼叫
在《SIP開源庫pjSIP簡介》中介紹了pjSIP的基本框架,本節對pjSIP中SIP的注冊與呼叫具體流程進行簡要說明。
注冊注銷
SIP通過register
進行注冊請求:
-
終端向VoIP服務器發送register請求:URL中包含注冊服務器的域名地址,To頭域中包含准備生成的地址記錄,Contact頭域中表明此次注冊所要綁定的地址(其中Expire表示綁定失效時長);
-
VoIP服務器返回401響應,要求用戶鑒權;
-
終端發送帶鑒權信息的注冊請求;
-
注冊完成;
SIP的注銷流程與注冊流程基本相同,只是Expires參數為0(標識注銷)
注冊接口
pjSIP中通過pjsua_acc_add來添加用戶信息並完成注冊;若不用默認的5060注冊端口,則需要增加代理設置,以便客戶端能正確地連接;注冊成功后會返回注冊賬戶id(非負數)。
-
-
-
-
-
-
pj_acc_id g_accId = -1;
-
-
int pjRegister(){
-
pjsua_acc_config accConfig;
-
psua_acc_config_default(&accConfig);
-
accConfig.id = pj_str( "sip:" PJ_User "@" PJ_Server);
-
accConfig.reg_uri = pj_str( "sip:" PJ_Server); // Comment, if not need register
-
-
// Cred
-
accConfig.cred_count = 1;
-
auto &cred = accConfig.cred_info[0];
-
cred.realm = pj_str(PJ_Server);
-
cred.scheme = pj_str( "digest");
-
cred.username = pj_str(PJ_User);
-
cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
-
cred.data = pj_str(PJ_Psw);
-
-
if(stod(PJ_Port) != 5060){
-
accConfig.proxy_cnt= 1;
-
accConfig.proxy[ 0]=pj_str("sip:" PJ_Server ":" PJ_Port)
-
}
-
-
auto nStatus = pjsua_acc_add(&accConfig, PJ_FALSE, &g_accId);
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
-
...
-
}
pjsua_acc_add主要是添加客戶端賬戶信息,后續呼叫時,就使用此賬戶信息;即使直呼(不需要注冊直接呼叫,此時不要設定reg_uri)也需用調用此接口增加賬戶信息。賬戶不再需要時,通過pjsua_acc_del來刪除賬戶(若注冊過,則會自動注銷)。
注冊事件
注冊完成后(無論成功還是失敗),都會觸發注冊狀態改變事件(初始化pjSUA時設定的Onregstate回調);在此事件中,即可獲取注冊狀態。
-
static void OnRegState(pjsua_acc_id accId){
-
pjsua_acc_info accInfo;
-
auto nStatus = pjsua_acc_get_info(accId, &accInfo);
-
-
bool bReged = accInfo.has_registration
-
&& (accInfo.expires > 0)
-
&& (accInfo.status / 100 == 2);
-
-
// To log wheather registered
-
}
電話呼叫
SIP終端通過向代理(如,VoIP Server)發invite
消息實現:
-
SIP終端A向代理發送invite請求:消息體中有終端A的媒體屬性SDP描述;
-
代理返回407響應,要求鑒權;
-
SIP終端A重發帶鑒權信息的invite請求;
-
代理返回
100 Tring
響應,並轉發invite請求到終端B; -
終端B振鈴,並返回(給代理)
180 Ringing
響應,代理再轉發給終端A; -
終端B摘機,返回
200 OK
響應:消息體中有終端B的媒體屬性SDP描述,代理再轉發給終端A; -
終端A返回ACK確認;
A與B注冊在不同代理的情形:
呼叫接口
pjSIP中通過pjsua_call_make_call實現電話呼叫,呼叫之前必須保證已添加了賬戶信息;通話完成后,通過pjsua_call_hangup/pjsua_call_hangup_all掛機,有來電時通過pjsua_call_answer接聽。
-
-
int pjMakeCall(){
-
auto toCall = pj_str("sip:" PJ_Called "@" PJ_Server)
-
auto nStatus = pjsua_call_make_call(g_accId, &toCall, NULL, NULL, NULL, NULL);
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
-
...
-
}
呼叫事件
呼叫狀態改變時,會觸發呼叫狀態改變事件(初始化pjSUA時設定的OnCallState回調);在此事件中可獲取具體通話狀態。
-
static void OnCallState(pjsua_call_id callId, pjsip_event *pEvent){
-
pjsua_call_info callInfo;
-
auto nStatus = pjsua_call_info(callId, &callInfo);
-
-
if(callInfo.state == PJSIP_INV_STATE_DISCONNECTED){
-
// Call hanguped
-
ToStopMedia(callInfo);
-
// ... other clear
-
}
-
}
通話真正開始(RTP媒體流建立),會觸發媒體狀態改變事件(初始化pjSUA時設定的OnMediaState回調),此時可以對媒體處理(如關聯麥克風、揚聲器等)。
-
static void OnMediaState(pjsua_call_id callId){
-
pjsua_call_info callInfo;
-
auto nStatus = pjsua_call_info(callId, &callInfo);
-
-
if(callInfo.media_status == PJSUA_CALL_MEDIA_ACTIVE){
-
// has started trans media
-
ToStartMedia(callInfo);
-
-
// 若只是與聲卡設備連接,執行連接0端口即可
-
// pjsua_conf_connect(ci.conf_slot, 0);
-
// pjsua_conf_connect(0, ci.conf_slot);
-
}
-
}
如若非直接通過聲卡收發媒體,則需要自己去創建媒體處理端口,具體如何處理后續章節再講。