如果有问题,请加QQ群 891339868 进行交流

上次在NUC972上移植好了pjsip库后,实现了一个简单的VOIP网关的功能,由于前一段时间再忙别的事情,没有来得及整理,今天忙里偷闲,总结一下,废话少说,直接步入正题。

pjsip库里面提供了好多例程,实现简单的VOIP功能还是挺简单的,主要包含以下几个部分:

1、初始化,废话少说,直接上代码:

int my_pjsua_init(void)
{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);/* Init pjsua */{pjsua_config cfg;pjsua_logging_config log_cfg;pjsua_media_config media_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;pjsua_media_config_default(&media_cfg);media_cfg.clock_rate = 8000;media_cfg.snd_clock_rate = 8000;media_cfg.no_vad = PJ_TRUE;media_cfg.ec_options = 2;media_cfg.quality = 5;media_cfg.channel_count = 2;status = pjsua_init(&cfg, &log_cfg, &media_cfg);if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);pj_str_t host = pj_str("speex");status = pjsua_codec_set_priority(&host, 0 );if (status != PJ_SUCCESS) error_exit("pjsua_codec_set_priority()", status);host = pj_str("G722");status = pjsua_codec_set_priority(&host, 0 );if (status != PJ_SUCCESS) error_exit("pjsua_codec_set_priority()", status);}/* Add UDP transport. */{pjsua_transport_config cfg;pjsua_transport_config_default(&cfg);cfg.port = 8060;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("*");//域名最好设置成*,要不然很容易注册不上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);}return 0;
}

这个是我的初始化函数,下面详细分析一下:

1、使用pjsua_create()这个函数创建一个sip用户代理;

2、对sip用户代理的基本配置、日志打印配置和媒体配置进行初始化,具体流程都是使用相对应的默认配置函数进行默认配置,分别为pjsua_config_default,pjsua_logging_config_default,pjsua_media_config_default。

基本配置的功能主要是配置在运行过程中,需要调用的回调函数,这些回调函数是库自动调度的,我这里使用了三个回调函数:

on_incoming_call:来电处理函数。来具体看一下代码:

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);
}

我这里使用了来电自动接听的功能,所以使用了pjsua_call_answer(call_id, 200, NULL, NULL)这个函数。

on_call_media_state:更改媒体状态回调函数。来具体看一下代码:

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);}
}

这个函数主要是在建立连接时使用。

on_call_state:调用当前呼叫状态回调函数,主要是当前呼叫状态发生状态时,库会自动调用该函数,来具体看一下代码:

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));
}

从代码中可以看出来,主要是打印当前呼叫的状态。

由于网关没有考虑自己主动挂断当前呼叫,所以没考虑其他回调函数。

接着来看一下日志打印配置,我这里配置的打印级别是4,这个数越大,级别越低,来看一下代码:

pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 4;

打印级别太高的话,控制台输出信息太多,挺讨厌的。

接着来看一下媒体配置信息,来看一下代码:

pjsua_media_config_default(&media_cfg);
media_cfg.clock_rate = 8000;
media_cfg.snd_clock_rate = 8000;
media_cfg.no_vad = PJ_TRUE;
media_cfg.ec_options = 2;
media_cfg.quality = 5;
media_cfg.channel_count = 2;

从代码上可以看出来,时钟和声音时钟都为8Khz,VAD(有效语音侦测功能)关闭,AEC算法选择的是webtrc,语音质量是5,最高10,双声道。这里重点说一下AEC算法和语音质量,NUC972性能有限,AEC算法的选择和语音质量的选择要慎重,不然的话会出现声音卡的现象,如果是其他高性能处理器,这个就不用考虑了。

到此为止,这三个配置基本上完成,就可以调用函数pjsua_init初始化了,如下代码所示:

 status = pjsua_init(&cfg, &log_cfg, &media_cfg);if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);

虽然现在基本的配置已经完成了,但是在NUC972上,还需要对编解码算法的支持做一下限制,因为NUC972是一个ARM9,工作频率只有300MHz,在运行speex算法和G722算法时,性能是不够的,会出现非常严重的卡顿现象,所以需要将这两种算法给禁止了,当然,如果使用其他平台可以根据自己的实际情况设置,具体的配置看一下下面的代码:

    pj_str_t host = pj_str("speex");status = pjsua_codec_set_priority(&host, 0 );if (status != PJ_SUCCESS) error_exit("pjsua_codec_set_priority()", status);host = pj_str("G722");status = pjsua_codec_set_priority(&host, 0 );if (status != PJ_SUCCESS) error_exit("pjsua_codec_set_priority()", status);

将speex和G722算法的优先级都设置为0,这两种算法就被禁止了。

接下来说一下关于数据传输的配置,如下面的代码所示:

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

从上面的代码中可以很容易的看出来,我设置的传输模式是UDP模式,使用的端口是8060。到这里,基本的配置就算完了,下面就可以启动sip的用户代理了,如下面的代码所示:

/* 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("*");//域名最好设置成*,要不然很容易注册不上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_start()是启动服务的函数,启动服务以后,要向sip服务器注册,如果目标服务器有当前的账号,就可以注册成功。

最后说一下在主函数中怎么使用pthread_create创建线程并调用pjsip相关的子函数。

一般情况下,系统需要实现多线程,这就牵扯到了pjsip线程与原有线程兼容性的问题,要想解决这一问题,就需要将使用pthread_create创建的线程注册到pjsip线程中,具体的做法看如下代码:

    pthread_t thread20;  pj_thread_desc desc;pj_bzero(desc, sizeof(desc));int iRet20;iRet20 = pthread_create(&thread20, PTHREAD_CREATE_JOINABLE,(void *)wait_for_hangup, NULL);if(iRet20){perror("pthread_create:thread20. \n");return 0;}pj_thread_t *thread = &thread20;pj_thread_register(NULL, desc, &thread);

我用pthread_create创建了一个线程以后,再使用pjsip库函数pj_thread_register想pjsip库注册一下就OK了!好了,说到这里,用NUC972实现的VOIP网关就可以工作了,下面看一下具体的工作效果:

1、开机运行,打印了一大堆东西,主要看最后几行:

出现这种提示就说明已经注册成功,本机的账户是104,sip服务器的URL是192.168.0.200:8060,设置的是来电自动接听。

2、来电自动接听,又是打印了一堆东西,乱七八糟的,具体的就先不贴了

3、对方挂断,依然是打印了一堆东西。

好了,nuc972上实现简单的自动接听的VOIP网关的功能已经完成。

在NUC972上利用pjsip实现VOIP网关相关推荐

  1. 在NUC972上移植pjsip库并使用PJSUA测试VOIP电话(二)

    如果有问题,请加QQ群 891339868 进行交流 昨天我已经把pjsip工程相关的库移植好,今天开始使用库里面自带的测试程序进行测试,在这之前需要搭建一个测试环境,我使用的是minsipserve ...

  2. 在NUC972上移植pjsip库并使用PJSUA测试VOIP电话(一)

    如果有问题,请加QQ群 891339868 进行交流 近段时间,突然对VOIP电话有了兴趣,想在NUC972的板子上测试一下VOIP电话,经过查阅资料,了解到VOIP电话是基于sip协议做的,所以对s ...

  3. QT开发pjsip的VOIP,A8平台运行

    QT开发pjsip的VOIP 开发环境 平台:A8 环境:Linux-3.0.8 实现功能:使用QT开发VOIP进行初始化.拨号.挂起 测试工具:minisipserver服务器 效果 界面: min ...

  4. openwrt上的asterisk的VOIP系统的建立

    原文地址:openwrt上的asterisk的VOIP系统的建立 作者:cliff 网络电话很便宜.自己搭一个网络电话系统更好玩. 这里写的是两年前我的网络电话系统.现在个人使用的系统简单得多了. 当 ...

  5. 如何在阿里云物联网平台上利用已有的 IoT Studio项目模板创建项目

    目录 前言 一.在阿里云找到已有的IoT Studio公开项目模板 二.选择公开项目模板 三.其他选择 结语 前言 前面的文章里,我们介绍了如何在阿里云物联网平台上添加LoRa节点设备,并让LoRa节 ...

  6. android自动发送dtmf,如何在Android上使用pjsip发送dtmf?

    我正在尝试在voip呼叫期间发送dtmf. 为了做到这一点我使用如何在Android上使用pjsip发送dtmf? mCall.dialDtmf(String.valueOf(dtmf)); MCAL ...

  7. 在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库

    在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库 根据调查,普通人产生的1.2万亿张图像可以通过电话或数码相机捕获.这样的图像的存储,尤其是以高分辨率的原始格式, ...

  8. 在Ubuntu 16.04.5 LTS上利用python中的PIL模块压缩一百多兆的单张图片实操

    在前面的博文中,我将300多张电影海报拼接为了一张103MB的巨幅图片,我想拿它做电脑桌面(1080P),但是这么多的图片,存储和加载是个麻烦事儿,需要将它压缩到几MB大小. 在Ubuntu 16.0 ...

  9. 在Linux上利用python获取本机ip

    下面介绍在Linux上利用python获取本机ip的方法. 经过网上调查, 发现大致有两种方法, 一种是调用shell脚本,另一种是利用python中的socket等模块来得到,下面是这两种方法的源码 ...

最新文章

  1. 应用程序启动器 标记为信任_为什么您今天不能信任应用程序-以及如何解决它...
  2. 基于深度迁移学习进行时间序列分类
  3. java基础之ConcurrentHashMap
  4. 使用volatile关键字的场景
  5. 上海理工大学计算机专硕分数线,热门专业=毕业高薪?曾经的人热门计算机,如今却“前程堪忧”...
  6. 北京大学计算机专业王腾,王腾(北京大学地球与空间科学学院助理教授)_百度百科...
  7. 【RobotStudio学习笔记】(一)软件的安装与初步测试
  8. 前端MVC学习笔记第二课
  9. matlab绘制正弦波
  10. codesys采用G代码实现圆弧插补和螺旋插补的可视化仿真
  11. Red Hat 9.0下载及安装
  12. 技术栈(technology stack)
  13. 计算机应用专业招聘试讲内容,广东文艺职业学院2018年第二批合同制人员招聘专业技能考核和试讲题目...
  14. Muse UI遇到的坑
  15. [数据结构与算法 DSA 林轩田] 1. Introduction to Data Structure and Algorithm
  16. 《有钱人和你想的不一样》 哈维·艾克 书评
  17. Logging initialized using configuration in jar:file:/home/lan/software/hive/lib/hive-common-1.2.1.ja
  18. 制度是绝情的,管理是无情的,执行是合情的
  19. 用Python语言绘制股市OBV指标效果
  20. 简历石沉大海?程序员简历到底该怎么写?

热门文章

  1. 用多任务网络探索单细胞数据
  2. 计算机基础知识和实践技能300分,2019年河北省高职单招考试十类 和对口电子电工类、计算机类联考 职业适应性测试(计算机基础知识和实践技能) 考试大纲...
  3. JavaScript上传附件对格式的限制
  4. visio如何固定地调整图形大小
  5. 从母亲做生意要会“混”的视角,看创业和工作
  6. 2021年高压电工复审模拟考试及高压电工实操考试视频
  7. html标题和段落一样大,HTML标题和段落
  8. 使用yum 安装插件报错:You could try using --skip-broken to work around the problem
  9. 「目前最好的文生视频AI」来了!做广告、电影都不在话下,网友:很丝滑
  10. 【Unity优化篇】 | Unity脚本代码优化策略,空引用快速检索、使用合适的数据结构、禁用脚本和对象等 性能优化方法