目录

1.来电接收的回调函数

2.用于处理PJSIP中通话状态改变的回调事件()

3.处理通话媒体状态改变的回调事件

4.错误信息显示以及退出应用程序

5.主函数

5.1 定义存储id和返回状态变量

5.2创建PJSIP

5.3校验URL

5.4初始化PJSUA库和日志配置

5.5 创建UDP传输通道

5.6 启动pjsua

5.7 设置SIP用户帐号信息并注册到SIP服务器

5.8发起呼叫

5.9等待用户输入

5.10 销毁


程序位于 /pjsip-apps/src/samples/simple_pjsua.c

流程图可以参考:

PJSIP学习笔记——从simple_pjsua.c示例程序了解PJSUA-LIB的基本使用流程_pjsua 注册_造梦工程师的博客-CSDN博客要了解pjsip的使用,simple_pjsua.c是一个很好的例子,虽然代码只有短短的172行,却展示了pjsua-lib层的完整使用流程、注册流程和基本呼叫流程。下面是学习过程中整理的simple_pjsua.c中的main函数主要流程:先来看看pjsip-apps/src/samples/simple_pjsua.c的main函数/* * main() * * ar_pjsua 注册https://blog.csdn.net/panjunbiao/article/details/8784231

1.来电接收的回调函数

/* Callback called by the library upon receiving incoming call */static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata){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);}

当接收到呼入电话时,PJSIP库会调用它。

它接收三个参数:acc_id是账户ID,call_id是通话ID,rdata是PJSIP的接收数据。

PJ_UNUSED_ARG();//忽略未使用的参数,避免编译器产生警告
PJ_LOG(3,(THIS_FILE, "Incoming call from %.*s!!", (int)ci.remote_info.slen, ci.remote_info.ptr));//使用PJSIP的日志功能,记录来自远程端的呼入电话信息

%.*s是格式化输出的一部分,它表示在日志中输出一个字符串。
(int)ci.remote_info.slen表示要输出的字符串的长度。
ci.remote_info.ptr是一个指向远程端呼入电话信息的指针

pjsua_call_answer(call_id, 200, NULL, NULL);//回应呼入电话//call_id是通话ID,200是状态码,NULL表示不使用任何附加头,NULL表示不使用任何应答体

2.用于处理PJSIP中通话状态改变的回调事件()

/*Callback called by the library when call's state has changed*/static 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(3,(THIS_FILE, "Call %d state=%.*s", call_id, (int)ci.state_text.slen, ci.state_text.ptr));}

它接收两个参数:call_id是通话ID,e是PJSIP事件。

主要是用于记录通话状态改变的信息。

3.处理通话媒体状态改变的回调事件

/* Callback called by the library when call's media state has changed*/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);}}

它接收一个参数:call_id是通话ID。
当通话的媒体状态变为活动状态时,会将通话的音频连接到PJSUA的会议桥,使声音可以通过音频设备进行输入和输出,从而实现通话功能。

pjsua_conf_connect(ci.conf_slot, 0); 

将通话的音频连接到PJSUA的会议桥中,其中ci.conf_slot是通话的会议槽(conference slot)。

pjsua_conf_connect(0, ci.conf_slot);

将PJSUA的会议桥输出连接到通话的音频,从而使声音可以通过音频设备输出

4.错误信息显示以及退出应用程序

/* Display error and exit application */static void error_exit(const char *title, pj_status_t status){pjsua_perror(THIS_FILE, title, status);pjsua_destroy();exit(1);}

用于显示错误信息并退出应用程序。

它接收两个参数:title是错误的标题或描述,status是PJSIP库的错误状态码。

pjsua_perror(THIS_FILE, title, status);

使用PJSUA库的错误打印功能,将错误信息输出到日志中

pjsua_destroy();

调用PJSUA库的销毁函数,用于释放库中的资源并清理环境

5.主函数

int main(int argc, char *argv[]){pjsua_acc_id acc_id;pj_status_t status;/* Create pjsua first! */status = pjsua_create();if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);/* If argument is specified, it's got to be a valid SIP URL */if (argc > 1) {status = pjsua_verify_url(argv[1]);if (status != PJ_SUCCESS) error_exit("Invalid URL in argv", status);}/* Init pjsua */{pjsua_config cfg;pjsua_logging_config log_cfg;pjsua_config_default(&cfg);cfg.cb.on_incoming_call = &on_incoming_call;cfg.cb.on_call_media_state = &on_call_media_state;cfg.cb.on_call_state = &on_call_state;pjsua_logging_config_default(&log_cfg);log_cfg.console_level = 4;status = pjsua_init(&cfg, &log_cfg, NULL);if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);}/* Add UDP transport. */{pjsua_transport_config cfg;pjsua_transport_config_default(&cfg);cfg.port = 5060;status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);if (status != PJ_SUCCESS) error_exit("Error creating transport", status);}/* Initialization is done, now start pjsua */status = pjsua_start();if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);/* Register to SIP server by creating SIP account. */{pjsua_acc_config cfg;pjsua_acc_config_default(&cfg);cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);cfg.reg_uri = pj_str("sip:" SIP_DOMAIN);cfg.cred_count = 1;cfg.cred_info[0].realm = pj_str(SIP_DOMAIN);cfg.cred_info[0].scheme = pj_str("digest");cfg.cred_info[0].username = pj_str(SIP_USER);cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;cfg.cred_info[0].data = pj_str(SIP_PASSWD);status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);if (status != PJ_SUCCESS) error_exit("Error adding account", status);}/* If URL is specified, make call to the URL. */if (argc > 1) {pj_str_t uri = pj_str(argv[1]);status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, NULL);if (status != PJ_SUCCESS) error_exit("Error making call", status);}/* Wait until user press "q" to quit. */for (;;) {char option[10];puts("Press 'h' to hangup all calls, 'q' to quit");if (fgets(option, sizeof(option), stdin) == NULL) {puts("EOF while reading stdin, will quit now..");break;}if (option[0] == 'q')break;if (option[0] == 'h')pjsua_call_hangup_all();}/* Destroy pjsua */pjsua_destroy();return 0;}

5.1 定义存储id和返回状态变量

//acc_id用于存储帐户ID,status用于存储PJSIP库函数的返回状态pjsua_acc_id acc_id;pj_status_t status;

5.2创建PJSIP

 /* Create pjsua first!*/status = pjsua_create();if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);

如果创建失败,调用error_exit()函数打印错误信息,并退出/

5.3校验URL

if (argc > 1) {status = pjsua_verify_url(argv[1]);if (status != PJ_SUCCESS) error_exit("Invalid URL in argv", status);}

argc > 1 , 说明命令行提供了一个SIP URL地址
调用pjsua_verify_url()函数检验URL是否合法

5.4初始化PJSUA库和日志配置

{pjsua_config cfg;pjsua_logging_config log_cfg; pjsua_config_default(&cfg);cfg.cb.on_incoming_call = &on_incoming_call;cfg.cb.on_call_media_state = &on_call_media_state; cfg.cb.on_call_state = &on_call_state; pjsua_logging_config_default(&log_cfg);log_cfg.console_level = 4; status = pjsua_init(&cfg, &log_cfg, NULL);if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);}

在这段代码里,创建了配置结构体和日志结构体,并设置回调函数来处理不同类型的事件。

cfg.cb.on_incoming_call = &on_incoming_call;//来电时被调用cfg.cb.on_call_media_state = &on_call_media_state; //在通话中的媒体状态改变时被调用,比如在通话过程中切换音频或视频。cfg.cb.on_call_state = &on_call_state; //会在通话状态改变时被调用,比如通话开始、挂断或暂停
log_cfg.console_level0: PJSUA_LOG_LEVEL_DISABLED (禁用日志)1: PJSUA_LOG_LEVEL_ERROR(错误级别)2: PJSUA_LOG_LEVEL_WARNING(警告级别)3: PJSUA_LOG_LEVEL_NOTICE (通知级别)4: PJSUA_LOG_LEVEL_INFO (信息级别)5: PJSUA_LOG_LEVEL_DEBUG(调试级别) 
初始化pjsuapjsua_init();

5.5 创建UDP传输通道

{pjsua_transport_config cfg; pjsua_transport_config_default(&cfg);cfg.port = 5060;status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);if (status != PJ_SUCCESS) error_exit("Error creating transport", status);}

添加UDP传输端口。创建了pjsua_transport_

config结构体并设置默认值,然后指定端口为5060,通过pjsua_transport_create()函数创建UDP传输。

5.6 启动pjsua

status = pjsua_start();if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);

5.7 设置SIP用户帐号信息并注册到SIP服务器

{pjsua_acc_config cfg; pjsua_acc_config_default(&cfg);cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);cfg.reg_uri = pj_str("sip:" SIP_DOMAIN);cfg.cred_count = 1;cfg.cred_info[0].realm = pj_str(SIP_DOMAIN);cfg.cred_info[0].scheme = pj_str("digest");cfg.cred_info[0].username = pj_str(SIP_USER);cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;cfg.cred_info[0].data = pj_str(SIP_PASSWD); status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);if (status != PJ_SUCCESS) error_exit("Error adding account", status);}

创建了pjsua_acc_config配置结构体并设置默认值,然后根据给定的SIP_USERSIP_DOMAIN配置帐户信息。最后通过pjsua_acc_add()函数添加帐户并注册。

5.8发起呼叫

if (argc > 1) {pj_str_t uri = pj_str(argv[1]);status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, NULL);if (status != PJ_SUCCESS) error_exit("Error making call", status);}

如果命令行参数argc大于1,说明命令行提供了一个SIP URL地址,通过pjsua_call_make_call()函数发起一个呼叫。

5.9等待用户输入

for (;;) {char option[10]; puts("Press 'h' to hangup all calls, 'q' to quit");if (fgets(option, sizeof(option), stdin) == NULL) {puts("EOF while reading stdin, will quit now..");break;} if (option[0] == 'q')break; if (option[0] == 'h')pjsua_call_hangup_all();}

进入循环,等待用户输入。用户可以输入’h’来挂断所有呼叫,输入’q’来退出程序。

5.10 销毁

pjsua_destroy();

在程序结束时,销毁PJSIP并释放资源