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;}
- Linux下编译pjproject-2.6并运行例程simple_pjsua
- Linux 下编译并运行C++程序
- Linux下编译并运行C程序
- Linux下编译并运行C程序
- Linux Linux下如何编译并运行C程序
- linux内核编译并在ubuntu下运行
- Linux下如何编译并运行C程序
- Linux下如何编译并运行C程序
- Linux下如何编译并运行C程序
- Linux下如何编译并运行C程序
- Linux下如何编译并运行C程序
- Linux 下编译并运行C语言程序
- Linux下如何编译并运行C程序
- linux下编辑并编译运行C/C++/python程序
- 如何在 Linux 下编译并运行 C语言
- Linux下如何编译并运行C程序
- Linux下如何编译并运行C程序
- Linux下如何编译并运行C程序
- HADDOP安装配置过程(新手初学)
- COJ-1867-John and Health rate
- webpack-dev-server的使用
- 王爽《汇编语言》课程设计二续(包含软盘操作)
- springboot 使用校验框架validation校验
- Linux下编译pjproject-2.6并运行例程simple_pjsua
- Json初步知识
- codevs 1222 信与信封问题(二分图匹配 可确定的关系)
- 虚拟机搭建CDH-第四讲-安装第一台虚拟机需要修改的
- 日夜间切换
- Dalvik虚拟机
- 操作系统实验——浅谈pthread库线程创建
- 在C++中使用gRPC编程(1-环境搭建)
- java语言基础(50)——Eclipse导入和导出jar包,制作jar包说明书