PJSIP使用流程,基于 simple_pjsua.c

目录

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_level

0: 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    (调试级别) 
初始化pjsua
pjsua_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并释放资源


 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值