最近研究了下PJSIP这个强大的VOIP开源库,简单的来记录下遇到一些问题和解决思路,最后附上完整的工程代码供参考
功能:
1.实现用户的注册
2.实现收发短信
3.实现通话功能
踩的坑
1.默认的UDP的端口是5060这个需要改变,需要使用代理设置
2.发送短信编码格式乱码 需要进行转码
3.通话没有声音 是没有添加 媒体改变的回调函数
直接放上代码了
/*
2020年10月14日 10:45:31
初始化UA
*/
#include <pjsua-lib/pjsua.h>
#include <string>
#include <comutil.h>
#pragma comment(lib, "comsuppw.lib")
#define THIS_FILE __FILE__
#define SIP_DOMAIN "JamesWu9527"
#define SIP_USER "JamesWu9527"
#define SIP_PASSWD "123456"
#define SIPURL "192.168.0.1"
std::wstring UTF8ToUnicode(const std::string& str)
{
int len = 0;
len = str.length();
int unicodeLen = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
wchar_t* pUnicode;
pUnicode = new wchar_t[unicodeLen + 1];
memset(pUnicode, 0, (unicodeLen + 1) * sizeof(wchar_t));
::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, (LPWSTR)pUnicode, unicodeLen);
std::wstring rt;
rt = (wchar_t*)pUnicode;
delete pUnicode;
return rt;
}
std::string UnicodeToUTF8(const std::wstring& str)
{
char* pElementText;
int iTextLen;
iTextLen = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
pElementText = new char[iTextLen + 1];
memset((void*)pElementText, 0, sizeof(char) * (iTextLen + 1));
::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, pElementText, iTextLen, NULL, NULL);
std::string strText;
strText = pElementText;
delete[] pElementText;
return strText;
}
std::string wstring2string(const std::wstring& ws)
{
_bstr_t t = ws.c_str();
char* pchar = (char*)t;
std::string result = pchar;
return result;
}
//收到短信的回调
void on_pager(pjsua_call_id call_id, const pj_str_t* from, const pj_str_t* to, const pj_str_t* contact,
const pj_str_t* mime_type, const pj_str_t* body)
{
PJ_LOG(2, (THIS_FILE, "call_id:%d from: %s to: %s contact:%s mime %s body: %s", call_id, from->ptr, to->ptr,
contact->ptr, mime_type->ptr, body->ptr));
//解析body的内容
std::string strResult = wstring2string(UTF8ToUnicode(body->ptr));
PJ_LOG(2, (THIS_FILE, "%s", strResult.c_str()));
}
//收到电话的回调
void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
pjsip_rx_data* rdata)
{
PJ_LOG(2, (THIS_FILE, "来信息了"));
pjsua_call_info ci;
PJ_UNUSED_ARG(acc_id);
PJ_UNUSED_ARG(rdata);
pjsua_call_get_info(call_id, &ci);
PJ_LOG(3, (THIS_FILE, "Incoming call from %.*s!!",
(int)ci.remote_info.slen,
ci.remote_info.ptr));
/* Automatically answer incoming calls with 200/OK */
pjsua_call_answer(call_id, 200, NULL, NULL);
}
//媒体状态发生改变时,会调用的函数
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) {
// When media is active, connect call to sound device.
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
}
}
//呼叫状态发生变化的时候回调函数
void on_call_state(pjsua_call_id call_id, pjsip_event* e)
{
pjsua_call_info ci;
PJ_UNUSED_ARG(e);
pjsua_call_get_info(call_id, &ci);
PJ_LOG(2, (THIS_FILE, "Call %d state=%.*s", call_id,
(int)ci.state_text.slen,
ci.state_text.ptr));
}
static pj_status_t app_init()
{
//Step1 创建PJSUA
pj_status_t status = pjsua_create();
if (status != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "error about app_init() function",status);
return status;
}
pjsua_config ua_cfg;
pjsua_config_default(&ua_cfg);
ua_cfg.cb.on_pager = &on_pager; //接收消息的回调函数
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; //呼叫状态
pjsua_logging_config log_cfg;
pjsua_logging_config_default(&log_cfg);
pjsua_media_config media_cfg;
pjsua_media_config_default(&media_cfg);
//Step2 初始化pjsua
status = pjsua_init(&ua_cfg, &log_cfg, &media_cfg);
if (status != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "error about pjsua_init() function", status);
return status;
}
//Step3 创建sip传输
pjsua_transport_config transp_cfg;
pjsua_transport_config_default(&transp_cfg);
transp_cfg.port = 6050;
transp_cfg.public_addr = pj_str(SIPURL);
pjsua_transport_id transp_id;
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transp_cfg, &transp_id);
if (status != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "error about pjsua_transport_create() function", status);
return status;
}
//Step4 运行pjsua
status = pjsua_start();
if (status != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "error about pjsua_start() function", status);
pjsua_destroy();
return status;
}
//Step5 注册账号
pjsua_acc_config acc_config;
pjsua_acc_config_default(&acc_config);
acc_config.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
acc_config.reg_uri = pj_str("sip:" SIP_DOMAIN);
acc_config.cred_count = 1;
//默认的UDP端口是试用的5060端口,如果需要试用其他端口 需要进行下面两句代码的设置 增加代理设置
acc_config.proxy_cnt = 1;
acc_config.proxy[0] = pj_str("sip:" SIPURL ":" "6050");
acc_config.cred_info[0].realm = pj_str(SIP_DOMAIN);
acc_config.cred_info[0].scheme = pj_str("digest");
acc_config.cred_info[0].username = pj_str(SIP_USER);
acc_config.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
acc_config.cred_info[0].data = pj_str(SIP_PASSWD);
pjsua_acc_id acc_id;
status = pjsua_acc_add(&acc_config, 0, &acc_id);
if (status != PJ_SUCCESS)
{
PJ_LOG(2, (__FILE__, "pjsua_acc_add() error"));
return status;
}
char option[10];
while (1)
{
puts("Press 'q' to quit");
if (fgets(option, sizeof(option), stdin) == NULL)
{
puts("EOF while reading stdin, will quit now..");
break;
}
if (option[0] == 'q') //退出系统
break;
else if (option[0] == 's') //发送短信
{
//发送短信
pj_str_t to = pj_str("sip:10010006@kinet");
std::string strcontent = UnicodeToUTF8(L"我是中文测试");
pj_str_t content = pj_str(const_cast<char *>(strcontent.c_str()));
status = pjsua_im_send(acc_id, &to, NULL, &content , NULL, NULL);
if (status != PJ_SUCCESS)
{
PJ_LOG(2, (__FILE__, "发送消息失败 %d",status));
break;
}
}
else if (option[0] == 'm')
{
//拨打电话
pj_str_t dst_uri = pj_str("sip:10010006@kinet");
status = pjsua_call_make_call(acc_id, &dst_uri, NULL, NULL, NULL, NULL);
if (status != PJ_SUCCESS)
{
PJ_LOG(2, (__FILE__, "拨打电话失败 %d", status));
break;
}
}
else if (option[0] == 'h') //挂断电话
{
pjsua_call_hangup_all();
}
}
pjsua_destroy();
system("pause");
}
int main()
{
app_init();
return 0;
}