Linux下编译pjproject-2.6并运行例程simple_pjsua

来源:互联网 发布:nat123端口映 编辑:程序博客网 时间:2024/06/07 05:03

近期项目中使用了sip协议进行音视频通话,百度的介绍是:
PJSIP同时支持语音、视频、状态呈现和即时通讯。PJSIP具有非常完善的文档,对开发者非常友好。
因特网电话(IP电话)正在向一种正式的商业电话模式演进,
SIP就是用来确保这种演进实现而需要的NGN(下一代网络)系列协议中重要的一员。支持H.264协议。
官方例程还是非常不错的,但是网上讲到使用官方例程的资料相对来讲还是比较少,并且对新手不太友好;
因为我是从头开始使用,就记录一下在Linux下从头开始编译到使用官方例程的过程;

官方下载

http://www.pjsip.org/download.htm
我是使用了最新的pjproject-2.6版本进行编译;
如果需要早期的版本,可以通过svn直接check出来
http://svn.pjsip.org/repos/pjproject
这里写图片描述

编译工程

这个版本是自带有Makefile文件并且使用configure不会自定生成Makefile
所以在下载后使用的时候还是有点惯性的删掉了Makefile想configure重新生成的时候失败了,其实通过configure脚本可以发现,其实2.6版本的configure是通过生成build.mak和config.status来进行相关的编译属性以及交叉编译配置的;

首先是根目录下:$ ./configure & make 并没能成功;
出现错误:

../../yuv/source/row_common.cc: In function ‘void libyuv::YuvPixel(uint8, uint8, uint8, uint8*, uint8*, uint8*, const libyuv::YuvConstants*)’:../../yuv/source/row_common.cc:1256: error: invalid types ‘const signed char __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1257: error: invalid types ‘const signed char __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1258: error: invalid types ‘const signed char __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1259: error: invalid types ‘const signed char __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1260: error: invalid types ‘const short int __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1261: error: invalid types ‘const short int __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1262: error: invalid types ‘const short int __vector__[int]’ for array subscript../../yuv/source/row_common.cc:1263: error: invalid types ‘const short int __vector__[int]’ for array subscriptmake[3]: *** [output/libyuv-x86_64-unknown-linux-gnu/row_common.o] Error 1make[3]: Leaving directory `/home/zzy/pjproject-2.6/pjproject-2.6/third_party/build/yuv'make[2]: *** [libyuv-x86_64-unknown-linux-gnu.a] Error 2make[2]: Leaving directory `/home/zzy/pjproject-2.6/pjproject-2.6/third_party/build/yuv'make[1]: *** [all] Error 1make[1]: Leaving directory `/home/zzy/pjproject-2.6/pjproject-2.6/third_party/build'make: *** [all] Error 1/pjproject-2.6/third_party/lib/libwebrtc-x86_64-unknown-linux-gnu.a(cpu_features.o):(.eh_frame+0x12): undefined reference to `__gxx_personality_v0'collect2: ld returned 1 exit statusmake[2]: *** [../bin/pjmedia-test-x86_64-unknown-linux-gnu] Error 1

这个报的是libyuv和libwebrtc库的错误,所以直接去掉相关的库的配置
$ ./configure –disable-libyuv –disable-libwebrtc & make

编译通过;

编译库的过程也是充满艰辛,网上搜索出来的结果基本上是清一色的:

# ./configure# make dep

还在得自己慢慢看 ./configure -help
找到
–disable-libyuv
–disable-libwebrtc
libyuv是Google开源的实现各种YUV与RGB之间相互转换、旋转、缩放的库;
YUV主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。
与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽(RGB要求三个独立的视频信号同时传输)

WebRTC(Web Real-Time Communication)项目的最终目的主要是
让Web开发者能够基于浏览器(Chrome\FireFox…)轻易快捷开发出丰富的实时多媒体应用

接着就把生成的库以及头文件进行安装方便拷贝出来使用:

$ ./configure  --disable-libyuv --disable-libwebrtc --prefix=$PWD/_install$ make & make install~/_install$ lsinclude  lib~/_install/lib$ lslibg7221codec-x86_64-unknown-linux-gnu.a        libpjmedia-codec-x86_64-unknown-linux-gnu.a    libpjsip-ua-x86_64-unknown-linux-gnu.a libresample-x86_64-unknown-linux-gnu.alibgsmcodec-x86_64-unknown-linux-gnu.a         libpjmedia-videodev-x86_64-unknown-linux-gnu.a  libpjsip-x86_64-unknown-linux-gnu.a     libspeex-x86_64-unknown-linux-gnu.alibilbccodec-x86_64-unknown-linux-gnu.a        libpjmedia-x86_64-unknown-linux-gnu.a          libpjsua2-x86_64-unknown-linux-gnu.a   libsrtp-x86_64-unknown-linux-gnu.alibpjlib-util-x86_64-unknown-linux-gnu.a        libpjnath-x86_64-unknown-linux-gnu.a           ibpjsua-x86_64-unknown-linux-gnu.a     pkgconfiglibpjmedia-audiodev-x86_64-unknown-linux-gnu.a  libpjsip-simple-x86_64-unknown-linux-gnu.a      libpj-x86_64-unknown-linux-gnu.a

include下面就比较多文件了,有13个文件夹……

交叉编译:
./configure –disable-libyuv –disable-libwebrtc –prefix=$PWD/_install –host=arm-none-linux-gnueabi

工程目录介绍

pjproject-2.6的工程目录:
主要介绍:
pjlib,pjlib-util,pjmedia,pjnath,pjsip,pjsip-apps 这几个目录结构一致整齐如下
|—-bin
|—-build
|—-docs
|—-include
|—-lib
|—-src
build目录下的Makefile文件,可以把在src下面的所有的c/cpp文件编译成可执行的二进制文件,输出到bin目录下;
开发者可以直接在bin目录下运行各个可执行文件查看到相关接口的使用;

测试官方Linux例程

这里使用的是pjsip-apps目录的官方例程了解一下2个设备之间的点对点通信:
进入build目录下$ make
则会把 pjsip-apps/pjsip-apps/src 的源代码编译出来,这里主要是pjsip-apps/pjsip-apps/src/samples/
这里下面的所有c/cpp文件将会被编译到pjsip-apps/bin/samples/x86_64-unknown-linux-gnu下面

调试simple_pjsua(源代码simple_pjsua.c)

这里需要有两台Ubuntu设备,开发板或者两台虚拟机 分别运行程序 ;

Ubuntu1(192.168.1.2) $ ./simple_pjsua
Ubuntu2(192.168.1.3) $ ./simple_pjsua sip:alice@192.168.1.2:5060
这里的Ubuntu2(192.168.1.3)运行程序带有参数,表示呼叫处于Ubuntu1(192.168.1.2)进程中的用户alice,当然这里同一个程序所以两个系统上都是使用了同一个用户名alice;

可以看到Ubuntu1(192.168.1.2)启动后,等待Ubuntu2(192.168.1.3)的呼入;
在Ubuntu1(192.168.1.2)可以看到来自Ubuntu2(192.168.1.3)的呼叫以及回复200

17:51:58.950            APP  ..Incoming call from <sip:alice@example.com>!!17:51:58.958            APP  .........Call 0 state=CONNECTING17:51:58.968            APP  ...Call 0 state=CONFIRMED17:51:58.968   pjsua_core.c  .RX 373 bytes Request msg BYE/cseq=13927 (rdata0x7f6790002858) from UDP 192.168.1.3:5060:

在Ubuntu2(192.168.1.3)可以看到来自Ubuntu1(192.168.1.2)的回复

17:51:29.411            APP  .......Call 0 state=CALLING17:51:29.429            APP  ......Call 0 state=CONNECTING17:51:29.431            APP  ......Call 0 state=CONFIRMED17:51:29.432   pjsua_core.c  .........TX 373 bytes Request msg BYE/cseq=13927 (tdta0x27ace80) to UDP 192.168.1.2:5060:

在Ubuntu2(192.168.1.3)上输入q主动挂断

Press 'h' to hangup all calls, 'q' to quitq18:22:17.824   pjsua_core.c !Shutting down, flags=0...18:22:17.825   pjsua_core.c  PJSUA state changed: RUNNING --> CLOSING18:22:17.835   pjsua_call.c !.Hangup all calls..18:22:17.836   pjsua_call.c  ..Call 0 hanging up: code=0..18:22:17.839   pjsua_core.c  ......TX 372 bytes Request msg BYE/cseq=9320 (tdta0x8fcc10) to UDP 192.168.1.2:5060:BYE sip:alice@192.168.1.2:5060;ob SIP/2.0Via: SIP/2.0/UDP 192.168.1.3:5060;rport;branch=z9hG4bKPjD.uTS.HSdsoXMVbObmK052zwAVp4k-tiMax-Forwards: 70From: sip:alice@example.com;tag=vWTPJAwlV8.fvlOQXE8wvGVqiTyu9PWOTo: sip:alice@192.168.1.2;tag=ZpoAP-tusbemk5oGnF-bsHEQc1pGtw3ACall-ID: ZgBtHs1h0wqWRCADyBlkIRupOlA-6spKCSeq: 9320 BYEContent-Length:  0

在Ubuntu1(192.168.1.2)看到通话被Ubuntu2(192.168.1.3)结束

--end msg--18:22:47.046   pjsua_core.c  .RX 372 bytes Request msg BYE/cseq=9320 (rdata0x7f0d40002858) from UDP 192.168.1.3:5060:BYE sip:alice@192.168.1.2:5060;ob SIP/2.0Via: SIP/2.0/UDP 192.168.1.3:5060;rport;branch=z9hG4bKPjD.uTS.HSdsoXMVbObmK052zwAVp4k-tiMax-Forwards: 70From: sip:alice@example.com;tag=vWTPJAwlV8.fvlOQXE8wvGVqiTyu9PWOTo: sip:alice@192.168.1.2;tag=ZpoAP-tusbemk5oGnF-bsHEQc1pGtw3ACall-ID: ZgBtHs1h0wqWRCADyBlkIRupOlA-6spKCSeq: 9320 BYEContent-Length:  0

例程中使用的主要的两个接口
pjsua_acc_add : 传入本机URL(sip:alice@example.com)进行注册
pjsua_call_make_call : 传入呼叫方的URL(sip:alice@192.168.1.2:5060)进行呼叫
附官方例程说明:

/** * simple_pjsua.c * * 这是一个非常简单但功能齐全的SIP用户代理, * 具有以下功能 *  - SIP 注册 *  - 发起一个呼叫和接听一个呼叫 *  - 音频媒体输出到声音设备上. * * 使用: *  - 发起一个新呼叫,启动应用程序simple_pjsua,传入参数 对方的URL进行连接 *    例如: *   ./simple_pjsua sip:user@remote * *  - 接收到呼入将自动回复200表示收到呼叫 * * 本程序在完成一次简单的呼叫后,就马上退出 */#include <pjsua-lib/pjsua.h>#define THIS_FILE   "APP"#define SIP_DOMAIN  "example.com"#define SIP_USER    "alice"#define SIP_PASSWD  "secret"/*  接收到新的呼入时,由底层库回调到上层 */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));    /* 自动回复呼入(200/OK) */    pjsua_call_answer(call_id, 200, NULL, NULL);}/*  由底层库回调到上层:底层已经改变了呼叫状态(接听,挂断,...) */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));}/* 由底层库回调到上层: 媒体(音视频)状态改变 */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) {    // 媒体连接启动时,连接到声音设备.    pjsua_conf_connect(ci.conf_slot, 0);    pjsua_conf_connect(0, ci.conf_slot);    }}/* 输出错误信息, 并退出应用程序 */static void error_exit(const char *title, pj_status_t status){    pjsua_perror(THIS_FILE, title, status);    pjsua_destroy();    exit(1);}/* * main() * * argv[1] 作为呼出的参数URL */int main(int argc, char *argv[]){    pjsua_acc_id acc_id;    pj_status_t status;    /* 首先创建一个PJSUA ! */    status = pjsua_create();    if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);    /* 如果带有参数(URL)用于呼出的,检测呼入的URL是否有效 */    if (argc > 1) {    status = pjsua_verify_url(argv[1]);    if (status != PJ_SUCCESS) error_exit("Invalid URL in argv", status);    }    /* 初始化 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);    }    /* 加入 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);    }    /* 初始化工作完成后,启动 PJSUA */    status = pjsua_start();    if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);    /* 创建SIP账号,并注册到SIP服务器上 */    {    pjsua_acc_config cfg;    pjsua_acc_config_default(&cfg);    cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN); // sip:alice@example.com    cfg.reg_uri = pj_str("sip:" SIP_DOMAIN); // sip:example.com    cfg.cred_count = 1;    cfg.cred_info[0].realm = pj_str(SIP_DOMAIN);//example.com    cfg.cred_info[0].scheme = pj_str("digest");    cfg.cred_info[0].username = pj_str(SIP_USER); //alice    cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;    cfg.cred_info[0].data = pj_str(SIP_PASSWD); //secret    status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);    if (status != PJ_SUCCESS) {        error_exit("Error adding account", status);        PJ_LOG(3,(THIS_FILE, "Error adding account %s",  cfg.reg_uri));    }    }    /* 如果有指定的要主动呼出URL,则使用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); // 呼出失败    } else {        PJ_LOG(3,(THIS_FILE, "Success making call %s",  argv[1])); //呼出成功    }    }    /* 循环等待,直到用户按下 "q" 键,则退出程序 */    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;}
0 0