1. 为什么值得花时间啃下 pjsip 的编译如果你正在做VoIP软电话、呼叫中心中间件、对讲系统或者任何需要SIP协议栈的产品,大概率绕不开pjsip这个库。它用C语言写成,体积小、依赖少、跨平台,从嵌入式设备到桌面客户端都能跑。但很多人第一次接触它,卡在第一步——编译安装。Linux下报一堆找不到头文件的错,Windows下CMake和Visual Studio版本对不上,编出来的库链接又缺符号。我前后在Linux和Windows上编过不下十次pjsip,踩过的坑足够写一篇避雷指南。这篇内容面向的是需要把pjsip集成到自己项目里的开发者,不管你是刚接触SIP协议的新手,还是想换一个更轻量协议栈的老手,都能照着走一遍。我会把Linux和Windows两条线的编译流程拆开讲清楚,包括依赖准备、配置参数怎么选、静态库和动态库的取舍、以及那些官方文档里不会写的细节。核心关键词就三个:pjsip编译、Linux构建、Windows构建,全文围绕它们展开。先说清楚pjsip到底是什么。它是一套完整的SIP协议栈实现,包含SIP信令、SDP协商、RTP媒体传输、音频设备抽象等模块。你可以只用它的SIP部分做信令服务器,也可以把媒体部分一起用起来做软电话。源码里带了pjlib(基础库)、pjlib-util(工具库)、pjnath(NAT穿透)、pjmedia(媒体处理)、pjsip(SIP核心)这几个子项目,编译时会按依赖顺序依次构建。理解这个层次关系很重要,后面排查编译错误时你就知道是哪个模块出的问题。2. 编译前的整体思路与方案选型2.1 源码获取与版本选择策略pjsip的源码托管在自己的版本管理服务器上,也有GitHub镜像。我建议直接用官方发布的稳定版压缩包,而不是拉最新开发分支。原因很简单:开发分支的构建脚本经常变动,可能今天能编明天就报错,而稳定版经过大量用户验证,踩坑概率低得多。版本号选择上,2.10到2.14这几个版本都比较成熟,2.15之后对CMake的支持更完善,如果你打算在Windows上用CMake构建,建议选2.15及以上。下载下来解压后,目录结构大致是这样:根目录下有pjlib、pjlib-util、pjnath、pjmedia、pjsip等子目录,还有一个aconfigure脚本和build.mak.in模板文件。Linux下用configure脚本生成Makefile,Windows下可以用自带的config脚本配合Visual Studio,也可以用CMake。两条路我都走过,下面分别说。注意:不要混用不同版本的子模块。有些人为了省事,从不同版本里各拿一部分拼在一起,结果链接时符号对不上,排查起来非常痛苦。要么全用同一个版本,要么全用同一个Git提交。2.2 Linux与Windows构建方案的核心差异Linux下的构建流程相对线性:装依赖、跑configure、make、make install。工具链就是gcc和make,变量少,出问题容易定位。Windows下就复杂一些,因为pjsip原生支持的是Visual Studio的nmake构建系统,但很多人习惯用CMake,而CMake支持是后来才加上的,不同版本行为不一致。我的建议是:如果你在Windows上做开发,优先用Visual Studio配合pjsip自带的config脚本生成工程文件,这样最稳。如果你非要用CMake,那就选2.15以上的版本,并且确保CMake和VS的版本匹配。下面两张表对比一下两条路的关键差异:对比项Linux构建Windows构建构建工具configure + makeconfig脚本 + nmake 或 CMake编译器gcc/g++MSVC依赖管理系统包管理器手动下载或vcpkg动态库后缀.so.dll静态库后缀.a.lib默认安装路径/usr/local源码目录下的lib和include2.3 静态库还是动态库,怎么选这是编译前必须想清楚的问题。静态库把pjsip的代码直接编进你的可执行文件,部署时不需要额外带一堆.so或.dll,但可执行文件体积会大不少,而且如果多个程序共用pjsip,每个都要单独编一份。动态库则相反,部署时要确保目标机器上有对应的库文件,但体积小、可复用。我的经验是:桌面客户端优先用静态库,因为用户机器上不一定有pjsip的运行库,静态链接省去部署麻烦。服务器端或者嵌入式环境优先用动态库,方便统一升级和维护。pjsip的configure脚本默认同时生成静态库和动态库,你可以通过参数控制只生成其中一种,减少编译时间。3. Linux下的完整编译实操3.1 依赖安装与基础环境检查在Ubuntu或Debian系上,先装好编译工具链和pjsip需要的开发库。pjsip的核心依赖不多,但如果你要启用音频、视频、SSL等功能,就需要额外装一些包。下面这条命令覆盖了大部分场景:sudo apt update sudo apt install -y build-essential make gcc g++ \ libasound2-dev libssl-dev libv4l-dev libavcodec-dev \ libavformat-dev libavutil-dev libswscale-dev \ libx11-dev libxext-dev libxrender-devlibasound2-dev是音频设备支持,libssl-dev是TLS加密,后面几个是视频和X11显示相关。如果你只做纯信令,不需要媒体,可以只装前两个。装完后用gcc --version和make --version确认工具链正常。提示:不同发行版的包名不一样。CentOS/RHEL系用yum install alsa-lib-devel openssl-devel,Arch系用pacman -S alsa-lib openssl。包名对不上时,先搜一下对应发行版的开发包命名规则。3.2 configure参数详解与推荐配置进入pjsip源码根目录,先跑一次./configure --help看看所有选项。选项很多,但常用的就那么十几个。下面是我常用的配置命令:./configure --prefix=/usr/local/pjsip \ --enable-shared --disable-static \ --disable-video --disable-sdl --disable-ffmpeg \ --disable-v4l2 --disable-openh264 \ --with-ssl=/usr/include/openssl \ CFLAGS="-O2 -fPIC" CXXFLAGS="-O2 -fPIC"逐项解释一下。--prefix指定安装路径,我习惯装到/usr/local/pjsip,不污染系统目录。--enable-shared --disable-static表示只生成动态库,如果你要静态库就反过来。--disable-video及后面几个是关掉视频相关功能,因为视频依赖多、编译慢,纯音频场景用不上。--with-ssl指定OpenSSL头文件路径,不指定的话configure会自动探测,但有时探测不准。CFLAGS里的-fPIC是生成位置无关代码,动态库必须加,静态库如果后面要链接进动态库也建议加。如果你需要视频功能,把--disable-video改成--enable-video,并确保ffmpeg和v4l2的开发包已装好。但我要提醒一句:pjsip的视频支持在不同版本间差异较大,2.10到2.12的视频模块bug较多,如果非用不可,建议选2.14以上。3.3 编译、安装与验证configure跑完后,会生成build.mak和各个子目录的Makefile。接下来直接:make dep && make clean && make -j$(nproc)make dep生成依赖关系,make clean清理旧对象文件,make -j并行编译加速。$(nproc)是CPU核心数,根据你机器调整。编译过程大概几分钟到十几分钟,取决于核心数和是否启用视频。编完后安装:sudo make install安装完成后,去/usr/local/pjsip下看看,应该有lib、include、bin三个目录。lib下是.so文件,include下是头文件,bin下是一些测试工具。验证一下库是否可用:ls /usr/local/pjsip/lib/ # 应该看到 libpjlib.so libpjlib-util.so libpjnath.so libpjmedia.so libpjsip.so 等再写一个最小的测试程序,包含pjsip.h并调用pjsip_endpt_create,编译链接一下,能过就说明安装没问题。链接时记得加-L/usr/local/pjsip/lib -lpjsip -lpjmedia -lpjnath -lpjlib-util -lpjlib,顺序不能乱,因为库之间有依赖关系。注意:Linux下动态库安装后,系统可能找不到。要么把/usr/local/pjsip/lib加到/etc/ld.so.conf里再跑ldconfig,要么在编译你的程序时用-Wl,-rpath指定运行时搜索路径。我推荐后者,不污染系统配置。4. Windows下的编译实操与避坑4.1 Visual Studio环境准备与源码配置Windows下我推荐用Visual Studio 2019或2022,社区版就够。安装时勾选“使用C++的桌面开发”工作负载,确保MSVC编译器和Windows SDK都装上。然后下载pjsip源码,解压到一个路径中不含空格和中文的目录,比如D:\dev\pjsip。这一点非常重要,pjsip的构建脚本对路径中的空格处理不好,放在Program Files下面必出问题。进入源码目录,找到pjlib\include\pj\config_site.h这个文件。默认它是空的或者只有注释,你需要手动加一些配置。我通常加这几行:#define PJ_CONFIG_SITE_H #define PJ_HAS_SSL_SOCK 1 #define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_OPENSSL #define PJMEDIA_HAS_VIDEO 0 #define PJMEDIA_HAS_FFMPEG 0PJ_HAS_SSL_SOCK开启TLS支持,PJMEDIA_HAS_VIDEO和PJMEDIA_HAS_FFMPEG关掉视频,减少依赖。如果你需要视频,把这两个改成1,并确保ffmpeg的开发库在编译时能被找到。4.2 用config脚本生成VS工程并编译pjsip自带一个config脚本,在Windows下用Visual Studio的命令行工具运行。打开“x64 Native Tools Command Prompt for VS 2022”,cd到源码目录,执行:config -enable-shared -disable-static这个脚本会生成build.mak和各个子项目的.vcxproj文件。然后:nmake clean nmakenmake会依次编译所有子项目。编译过程中如果报“找不到openssl头文件”,说明你的OpenSSL路径没配好。可以在config时加--with-ssl=C:\path\to\openssl,或者把OpenSSL的include和lib路径加到系统环境变量里。编完后,库文件在lib目录下,头文件在各子项目的include目录下。你可以直接把整个pjsip目录作为你项目的附加包含目录和附加库目录,也可以把需要的头文件和lib拷出来单独管理。4.3 CMake构建的注意事项如果你坚持用CMake,流程是这样的:mkdir build && cd build cmake .. -G "Visual Studio 17 2022" -A x64 ^ -DCMAKE_INSTALL_PREFIX=D:\dev\pjsip-install ^ -DPJ_ENABLE_SHARED=ON -DPJ_ENABLE_STATIC=OFF cmake --build . --config Release cmake --install . --config Release但CMake构建有几个坑。第一,pjsip的CMake支持在不同版本间差异大,2.15之前的版本可能缺少某些子项目的CMakeLists.txt,导致构建不完整。第二,CMake生成的工程默认可能不包含SSL配置,需要手动在CMakeCache里加PJ_HAS_SSL_SOCK。第三,CMake安装出来的目录结构和config脚本不一样,头文件路径可能对不上。我的建议是:能用config脚本就用config脚本,CMake只在你有特殊需求(比如要集成到已有的CMake项目里)时才用。如果非用CMake不可,选2.15以上版本,并且仔细检查生成的库是否完整。提示:Windows下编译pjsip时,如果遇到“无法打开源文件 pj/config_site.h”的错误,说明你的config_site.h没放对位置。它必须在pjlib\include\pj\目录下,而不是源码根目录。5. 常见编译问题与排查速查5.1 Linux下典型报错与解决报错一:configure: error: cannot find openssl原因通常是OpenSSL开发包没装,或者装了但configure找不到。先确认libssl-dev已安装,然后检查/usr/include/openssl/ssl.h是否存在。如果存在但configure还是报错,手动指定路径:--with-ssl=/usr/include/openssl。报错二:undefined reference to 'SSL_CTX_new'这是链接时找不到OpenSSL库。编译你的程序时加-lssl -lcrypto,并且确保这两个库在pjsip库之后。链接顺序是:你的代码 → pjsip库 → OpenSSL库 → 系统库。报错三:error: 'PJ_HAS_SSL_SOCK' undeclared说明config_site.h没生效。检查pjlib/include/pj/config_site.h是否存在,内容是否正确。有时候configure会覆盖这个文件,编译前再确认一遍。5.2 Windows下典型报错与解决报错一:fatal error C1083: 无法打开包括文件: "pj/config_site.h"config_site.h位置不对或内容为空。确保它在pjlib\include\pj\下,并且至少包含#define PJ_CONFIG_SITE_H这一行。报错二:LNK2019: 无法解析的外部符号通常是库链接顺序不对,或者漏了某个子库。Windows下链接顺序是:pjsip.lib→pjmedia.lib→pjnath.lib→pjlib-util.lib→pjlib.lib。如果用了SSL,再加libssl.lib和libcrypto.lib。报错三:nmake: fatal error U1077: "cl.exe" 返回代码 "0x2"这是编译器返回了错误,但nmake没显示具体原因。往上翻日志,找到第一个真正的错误。常见原因是源码路径含空格、MSVC版本不匹配、或者某个源文件编码问题。5.3 跨平台编译的通用避坑清单问题类型典型表现排查方向路径问题找不到头文件/库检查路径是否含空格、中文版本不匹配符号未定义、结构体大小不一致统一所有子模块版本依赖缺失configure报错、链接失败确认开发包已安装配置未生效功能未启用、宏未定义检查config_site.h链接顺序错误未定义符号按依赖顺序排列库注意:编译pjsip时,日志会非常长。不要被后面的错误淹没,永远从第一个错误开始排查。后面的错误往往是第一个错误引发的连锁反应。6. 编译完成后的集成与验证6.1 在你的项目中引用pjsipLinux下,在你的Makefile或CMakeLists.txt里加:find_package(PkgConfig REQUIRED) pkg_check_modules(PJSIP REQUIRED pjsip) include_directories(${PJSIP_INCLUDE_DIRS}) target_link_libraries(your_target ${PJSIP_LIBRARIES})但pjsip默认不生成.pc文件,你可能需要手动写一个,或者直接用绝对路径。我通常直接在CMake里写:set(PJSIP_ROOT /usr/local/pjsip) include_directories(${PJSIP_ROOT}/include) link_directories(${PJSIP_ROOT}/lib) target_link_libraries(your_target pjsip pjmedia pjnath pjlib-util pjlib)Windows下类似,把include和lib目录加到项目属性里,然后在链接器输入里加上那几个.lib文件。6.2 最小验证程序的编写与运行写一个最简单的程序,初始化pjsip并创建一个SIP端点:#include <pjsip.h> #include <pjlib.h> int main() { pj_caching_pool cp; pjsip_endpoint *endpt; pj_init(); pj_caching_pool_init(&cp, NULL, 0); pj_status_t status = pjsip_endpt_create(&cp.factory, NULL, &endpt); if (status != PJ_SUCCESS) { PJ_LOG(1, ("main", "创建端点失败: %d", status)); return 1; } PJ_LOG(1, ("main", "pjsip端点创建成功")); pjsip_endpt_destroy(endpt); pj_caching_pool_destroy(&cp); return 0; }编译命令(Linux):gcc test.c -o test -I/usr/local/pjsip/include \ -L/usr/local/pjsip/lib \ -lpjsip -lpjmedia -lpjnath -lpjlib-util -lpjlib \ -lssl -lcrypto -lpthread -lm运行后如果输出“pjsip端点创建成功”,说明整个编译安装链路没问题。6.3 性能与体积的取舍经验最后分享几个实测数据。在Ubuntu 20.04、i5-10400、8核并行编译下,关闭视频、只开SSL,完整编译大约3分钟。生成的动态库总大小约8MB,静态库约25MB。Windows下用VS2022、Release模式,编译约5分钟,动态库约6MB,静态库约20MB。如果你要减小体积,可以在configure时加--disable-speex --disable-g7221 --disable-ilbc等参数关掉不用的编解码器。但要注意,关掉某些编解码器后,如果对端只支持这些编码,通话会协商失败。我的建议是:先全开编一遍,确认功能正常,再根据实际需要裁剪。不要一上来就追求最小体积,那样排查问题会很痛苦。提示:pjsip的编译产物里,bin目录下有一些测试工具,比如pjsua。你可以用pjsua快速验证SIP功能是否正常,不需要自己写代码。pjsua --help能看到所有参数。7. 我踩过的几个印象深刻的坑第一个坑是Linux下编译时没加-fPIC,结果动态库链接时报“relocation R_X86_64_32 against symbol”错误。这个错误的意思是代码不是位置无关的,不能放进共享库。解决办法就是在CFLAGS里加-fPIC,然后make clean重新编。我当初不知道,重编了三次才找到原因。第二个坑是Windows下用CMake构建时,生成的库缺少pjsua相关符号。后来发现是CMakeLists.txt里没有包含pjsua子目录,需要手动在CMake配置里加-DPJ_BUILD_PJSUA=ON。这个选项在官方文档里没写,是我翻CMakeLists.txt源码找到的。第三个坑是跨平台编译时,Linux下编的静态库拿到Windows下用,链接报错。这个不用多说,静态库不跨平台,必须各编各的。但有些人会把头文件混用,头文件虽然跨平台,但里面的宏定义可能依赖平台,混用也会出问题。第四个坑是编译时开了-O3优化,结果某些SIP消息解析出现异常。后来降到-O2就正常了。pjsip的代码里有一些依赖内存布局的地方,过高的优化级别可能导致未定义行为。我现在的习惯是统一用-O2,不追求极致优化。这些经验告诉我,编译pjsip这件事,官方文档只能带你走通主流程,真正的坑都在细节里。多编几次,把每次的错误记下来,慢慢就形成自己的排查套路了。
1. 为什么值得花时间啃下 pjsip 的编译如果你正在做VoIP软电话、呼叫中心中间件、对讲系统或者任何需要SIP协议栈的产品,大概率绕不开pjsip这个库。它用C语言写成,体积小、依赖少、跨平台,从嵌入式设备到桌面客户端都能跑。但很多人第一次接触它,卡在第一步——编译安装。Linux下报一堆找不到头文件的错,Windows下CMake和Visual Studio版本对不上,编出来的库链接又缺符号。我前后在Linux和Windows上编过不下十次pjsip,踩过的坑足够写一篇避雷指南。这篇内容面向的是需要把pjsip集成到自己项目里的开发者,不管你是刚接触SIP协议的新手,还是想换一个更轻量协议栈的老手,都能照着走一遍。我会把Linux和Windows两条线的编译流程拆开讲清楚,包括依赖准备、配置参数怎么选、静态库和动态库的取舍、以及那些官方文档里不会写的细节。核心关键词就三个:pjsip编译、Linux构建、Windows构建,全文围绕它们展开。先说清楚pjsip到底是什么。它是一套完整的SIP协议栈实现,包含SIP信令、SDP协商、RTP媒体传输、音频设备抽象等模块。你可以只用它的SIP部分做信令服务器,也可以把媒体部分一起用起来做软电话。源码里带了pjlib(基础库)、pjlib-util(工具库)、pjnath(NAT穿透)、pjmedia(媒体处理)、pjsip(SIP核心)这几个子项目,编译时会按依赖顺序依次构建。理解这个层次关系很重要,后面排查编译错误时你就知道是哪个模块出的问题。2. 编译前的整体思路与方案选型2.1 源码获取与版本选择策略pjsip的源码托管在自己的版本管理服务器上,也有GitHub镜像。我建议直接用官方发布的稳定版压缩包,而不是拉最新开发分支。原因很简单:开发分支的构建脚本经常变动,可能今天能编明天就报错,而稳定版经过大量用户验证,踩坑概率低得多。版本号选择上,2.10到2.14这几个版本都比较成熟,2.15之后对CMake的支持更完善,如果你打算在Windows上用CMake构建,建议选2.15及以上。下载下来解压后,目录结构大致是这样:根目录下有pjlib、pjlib-util、pjnath、pjmedia、pjsip等子目录,还有一个aconfigure脚本和build.mak.in模板文件。Linux下用configure脚本生成Makefile,Windows下可以用自带的config脚本配合Visual Studio,也可以用CMake。两条路我都走过,下面分别说。注意:不要混用不同版本的子模块。有些人为了省事,从不同版本里各拿一部分拼在一起,结果链接时符号对不上,排查起来非常痛苦。要么全用同一个版本,要么全用同一个Git提交。2.2 Linux与Windows构建方案的核心差异Linux下的构建流程相对线性:装依赖、跑configure、make、make install。工具链就是gcc和make,变量少,出问题容易定位。Windows下就复杂一些,因为pjsip原生支持的是Visual Studio的nmake构建系统,但很多人习惯用CMake,而CMake支持是后来才加上的,不同版本行为不一致。我的建议是:如果你在Windows上做开发,优先用Visual Studio配合pjsip自带的config脚本生成工程文件,这样最稳。如果你非要用CMake,那就选2.15以上的版本,并且确保CMake和VS的版本匹配。下面两张表对比一下两条路的关键差异:对比项Linux构建Windows构建构建工具configure + makeconfig脚本 + nmake 或 CMake编译器gcc/g++MSVC依赖管理系统包管理器手动下载或vcpkg动态库后缀.so.dll静态库后缀.a.lib默认安装路径/usr/local源码目录下的lib和include2.3 静态库还是动态库,怎么选这是编译前必须想清楚的问题。静态库把pjsip的代码直接编进你的可执行文件,部署时不需要额外带一堆.so或.dll,但可执行文件体积会大不少,而且如果多个程序共用pjsip,每个都要单独编一份。动态库则相反,部署时要确保目标机器上有对应的库文件,但体积小、可复用。我的经验是:桌面客户端优先用静态库,因为用户机器上不一定有pjsip的运行库,静态链接省去部署麻烦。服务器端或者嵌入式环境优先用动态库,方便统一升级和维护。pjsip的configure脚本默认同时生成静态库和动态库,你可以通过参数控制只生成其中一种,减少编译时间。3. Linux下的完整编译实操3.1 依赖安装与基础环境检查在Ubuntu或Debian系上,先装好编译工具链和pjsip需要的开发库。pjsip的核心依赖不多,但如果你要启用音频、视频、SSL等功能,就需要额外装一些包。下面这条命令覆盖了大部分场景:sudo apt update sudo apt install -y build-essential make gcc g++ \ libasound2-dev libssl-dev libv4l-dev libavcodec-dev \ libavformat-dev libavutil-dev libswscale-dev \ libx11-dev libxext-dev libxrender-devlibasound2-dev是音频设备支持,libssl-dev是TLS加密,后面几个是视频和X11显示相关。如果你只做纯信令,不需要媒体,可以只装前两个。装完后用gcc --version和make --version确认工具链正常。提示:不同发行版的包名不一样。CentOS/RHEL系用yum install alsa-lib-devel openssl-devel,Arch系用pacman -S alsa-lib openssl。包名对不上时,先搜一下对应发行版的开发包命名规则。3.2 configure参数详解与推荐配置进入pjsip源码根目录,先跑一次./configure --help看看所有选项。选项很多,但常用的就那么十几个。下面是我常用的配置命令:./configure --prefix=/usr/local/pjsip \ --enable-shared --disable-static \ --disable-video --disable-sdl --disable-ffmpeg \ --disable-v4l2 --disable-openh264 \ --with-ssl=/usr/include/openssl \ CFLAGS="-O2 -fPIC" CXXFLAGS="-O2 -fPIC"逐项解释一下。--prefix指定安装路径,我习惯装到/usr/local/pjsip,不污染系统目录。--enable-shared --disable-static表示只生成动态库,如果你要静态库就反过来。--disable-video及后面几个是关掉视频相关功能,因为视频依赖多、编译慢,纯音频场景用不上。--with-ssl指定OpenSSL头文件路径,不指定的话configure会自动探测,但有时探测不准。CFLAGS里的-fPIC是生成位置无关代码,动态库必须加,静态库如果后面要链接进动态库也建议加。如果你需要视频功能,把--disable-video改成--enable-video,并确保ffmpeg和v4l2的开发包已装好。但我要提醒一句:pjsip的视频支持在不同版本间差异较大,2.10到2.12的视频模块bug较多,如果非用不可,建议选2.14以上。3.3 编译、安装与验证configure跑完后,会生成build.mak和各个子目录的Makefile。接下来直接:make dep && make clean && make -j$(nproc)make dep生成依赖关系,make clean清理旧对象文件,make -j并行编译加速。$(nproc)是CPU核心数,根据你机器调整。编译过程大概几分钟到十几分钟,取决于核心数和是否启用视频。编完后安装:sudo make install安装完成后,去/usr/local/pjsip下看看,应该有lib、include、bin三个目录。lib下是.so文件,include下是头文件,bin下是一些测试工具。验证一下库是否可用:ls /usr/local/pjsip/lib/ # 应该看到 libpjlib.so libpjlib-util.so libpjnath.so libpjmedia.so libpjsip.so 等再写一个最小的测试程序,包含pjsip.h并调用pjsip_endpt_create,编译链接一下,能过就说明安装没问题。链接时记得加-L/usr/local/pjsip/lib -lpjsip -lpjmedia -lpjnath -lpjlib-util -lpjlib,顺序不能乱,因为库之间有依赖关系。注意:Linux下动态库安装后,系统可能找不到。要么把/usr/local/pjsip/lib加到/etc/ld.so.conf里再跑ldconfig,要么在编译你的程序时用-Wl,-rpath指定运行时搜索路径。我推荐后者,不污染系统配置。4. Windows下的编译实操与避坑4.1 Visual Studio环境准备与源码配置Windows下我推荐用Visual Studio 2019或2022,社区版就够。安装时勾选“使用C++的桌面开发”工作负载,确保MSVC编译器和Windows SDK都装上。然后下载pjsip源码,解压到一个路径中不含空格和中文的目录,比如D:\dev\pjsip。这一点非常重要,pjsip的构建脚本对路径中的空格处理不好,放在Program Files下面必出问题。进入源码目录,找到pjlib\include\pj\config_site.h这个文件。默认它是空的或者只有注释,你需要手动加一些配置。我通常加这几行:#define PJ_CONFIG_SITE_H #define PJ_HAS_SSL_SOCK 1 #define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_OPENSSL #define PJMEDIA_HAS_VIDEO 0 #define PJMEDIA_HAS_FFMPEG 0PJ_HAS_SSL_SOCK开启TLS支持,PJMEDIA_HAS_VIDEO和PJMEDIA_HAS_FFMPEG关掉视频,减少依赖。如果你需要视频,把这两个改成1,并确保ffmpeg的开发库在编译时能被找到。4.2 用config脚本生成VS工程并编译pjsip自带一个config脚本,在Windows下用Visual Studio的命令行工具运行。打开“x64 Native Tools Command Prompt for VS 2022”,cd到源码目录,执行:config -enable-shared -disable-static这个脚本会生成build.mak和各个子项目的.vcxproj文件。然后:nmake clean nmakenmake会依次编译所有子项目。编译过程中如果报“找不到openssl头文件”,说明你的OpenSSL路径没配好。可以在config时加--with-ssl=C:\path\to\openssl,或者把OpenSSL的include和lib路径加到系统环境变量里。编完后,库文件在lib目录下,头文件在各子项目的include目录下。你可以直接把整个pjsip目录作为你项目的附加包含目录和附加库目录,也可以把需要的头文件和lib拷出来单独管理。4.3 CMake构建的注意事项如果你坚持用CMake,流程是这样的:mkdir build && cd build cmake .. -G "Visual Studio 17 2022" -A x64 ^ -DCMAKE_INSTALL_PREFIX=D:\dev\pjsip-install ^ -DPJ_ENABLE_SHARED=ON -DPJ_ENABLE_STATIC=OFF cmake --build . --config Release cmake --install . --config Release但CMake构建有几个坑。第一,pjsip的CMake支持在不同版本间差异大,2.15之前的版本可能缺少某些子项目的CMakeLists.txt,导致构建不完整。第二,CMake生成的工程默认可能不包含SSL配置,需要手动在CMakeCache里加PJ_HAS_SSL_SOCK。第三,CMake安装出来的目录结构和config脚本不一样,头文件路径可能对不上。我的建议是:能用config脚本就用config脚本,CMake只在你有特殊需求(比如要集成到已有的CMake项目里)时才用。如果非用CMake不可,选2.15以上版本,并且仔细检查生成的库是否完整。提示:Windows下编译pjsip时,如果遇到“无法打开源文件 pj/config_site.h”的错误,说明你的config_site.h没放对位置。它必须在pjlib\include\pj\目录下,而不是源码根目录。5. 常见编译问题与排查速查5.1 Linux下典型报错与解决报错一:configure: error: cannot find openssl原因通常是OpenSSL开发包没装,或者装了但configure找不到。先确认libssl-dev已安装,然后检查/usr/include/openssl/ssl.h是否存在。如果存在但configure还是报错,手动指定路径:--with-ssl=/usr/include/openssl。报错二:undefined reference to 'SSL_CTX_new'这是链接时找不到OpenSSL库。编译你的程序时加-lssl -lcrypto,并且确保这两个库在pjsip库之后。链接顺序是:你的代码 → pjsip库 → OpenSSL库 → 系统库。报错三:error: 'PJ_HAS_SSL_SOCK' undeclared说明config_site.h没生效。检查pjlib/include/pj/config_site.h是否存在,内容是否正确。有时候configure会覆盖这个文件,编译前再确认一遍。5.2 Windows下典型报错与解决报错一:fatal error C1083: 无法打开包括文件: "pj/config_site.h"config_site.h位置不对或内容为空。确保它在pjlib\include\pj\下,并且至少包含#define PJ_CONFIG_SITE_H这一行。报错二:LNK2019: 无法解析的外部符号通常是库链接顺序不对,或者漏了某个子库。Windows下链接顺序是:pjsip.lib→pjmedia.lib→pjnath.lib→pjlib-util.lib→pjlib.lib。如果用了SSL,再加libssl.lib和libcrypto.lib。报错三:nmake: fatal error U1077: "cl.exe" 返回代码 "0x2"这是编译器返回了错误,但nmake没显示具体原因。往上翻日志,找到第一个真正的错误。常见原因是源码路径含空格、MSVC版本不匹配、或者某个源文件编码问题。5.3 跨平台编译的通用避坑清单问题类型典型表现排查方向路径问题找不到头文件/库检查路径是否含空格、中文版本不匹配符号未定义、结构体大小不一致统一所有子模块版本依赖缺失configure报错、链接失败确认开发包已安装配置未生效功能未启用、宏未定义检查config_site.h链接顺序错误未定义符号按依赖顺序排列库注意:编译pjsip时,日志会非常长。不要被后面的错误淹没,永远从第一个错误开始排查。后面的错误往往是第一个错误引发的连锁反应。6. 编译完成后的集成与验证6.1 在你的项目中引用pjsipLinux下,在你的Makefile或CMakeLists.txt里加:find_package(PkgConfig REQUIRED) pkg_check_modules(PJSIP REQUIRED pjsip) include_directories(${PJSIP_INCLUDE_DIRS}) target_link_libraries(your_target ${PJSIP_LIBRARIES})但pjsip默认不生成.pc文件,你可能需要手动写一个,或者直接用绝对路径。我通常直接在CMake里写:set(PJSIP_ROOT /usr/local/pjsip) include_directories(${PJSIP_ROOT}/include) link_directories(${PJSIP_ROOT}/lib) target_link_libraries(your_target pjsip pjmedia pjnath pjlib-util pjlib)Windows下类似,把include和lib目录加到项目属性里,然后在链接器输入里加上那几个.lib文件。6.2 最小验证程序的编写与运行写一个最简单的程序,初始化pjsip并创建一个SIP端点:#include <pjsip.h> #include <pjlib.h> int main() { pj_caching_pool cp; pjsip_endpoint *endpt; pj_init(); pj_caching_pool_init(&cp, NULL, 0); pj_status_t status = pjsip_endpt_create(&cp.factory, NULL, &endpt); if (status != PJ_SUCCESS) { PJ_LOG(1, ("main", "创建端点失败: %d", status)); return 1; } PJ_LOG(1, ("main", "pjsip端点创建成功")); pjsip_endpt_destroy(endpt); pj_caching_pool_destroy(&cp); return 0; }编译命令(Linux):gcc test.c -o test -I/usr/local/pjsip/include \ -L/usr/local/pjsip/lib \ -lpjsip -lpjmedia -lpjnath -lpjlib-util -lpjlib \ -lssl -lcrypto -lpthread -lm运行后如果输出“pjsip端点创建成功”,说明整个编译安装链路没问题。6.3 性能与体积的取舍经验最后分享几个实测数据。在Ubuntu 20.04、i5-10400、8核并行编译下,关闭视频、只开SSL,完整编译大约3分钟。生成的动态库总大小约8MB,静态库约25MB。Windows下用VS2022、Release模式,编译约5分钟,动态库约6MB,静态库约20MB。如果你要减小体积,可以在configure时加--disable-speex --disable-g7221 --disable-ilbc等参数关掉不用的编解码器。但要注意,关掉某些编解码器后,如果对端只支持这些编码,通话会协商失败。我的建议是:先全开编一遍,确认功能正常,再根据实际需要裁剪。不要一上来就追求最小体积,那样排查问题会很痛苦。提示:pjsip的编译产物里,bin目录下有一些测试工具,比如pjsua。你可以用pjsua快速验证SIP功能是否正常,不需要自己写代码。pjsua --help能看到所有参数。7. 我踩过的几个印象深刻的坑第一个坑是Linux下编译时没加-fPIC,结果动态库链接时报“relocation R_X86_64_32 against symbol”错误。这个错误的意思是代码不是位置无关的,不能放进共享库。解决办法就是在CFLAGS里加-fPIC,然后make clean重新编。我当初不知道,重编了三次才找到原因。第二个坑是Windows下用CMake构建时,生成的库缺少pjsua相关符号。后来发现是CMakeLists.txt里没有包含pjsua子目录,需要手动在CMake配置里加-DPJ_BUILD_PJSUA=ON。这个选项在官方文档里没写,是我翻CMakeLists.txt源码找到的。第三个坑是跨平台编译时,Linux下编的静态库拿到Windows下用,链接报错。这个不用多说,静态库不跨平台,必须各编各的。但有些人会把头文件混用,头文件虽然跨平台,但里面的宏定义可能依赖平台,混用也会出问题。第四个坑是编译时开了-O3优化,结果某些SIP消息解析出现异常。后来降到-O2就正常了。pjsip的代码里有一些依赖内存布局的地方,过高的优化级别可能导致未定义行为。我现在的习惯是统一用-O2,不追求极致优化。这些经验告诉我,编译pjsip这件事,官方文档只能带你走通主流程,真正的坑都在细节里。多编几次,把每次的错误记下来,慢慢就形成自己的排查套路了。