PJSIP交叉编译

本文详细介绍了一种在嵌入式环境下进行音视频开发的方法,包括安装必要的依赖库、交叉编译asound库、编译pjsip库,以及如何使用pjsip静态库编译一个demo。该demo实现了SIP电话的基本功能,如接听来电、注册SIP服务器等。

一、安装一些必要的依赖库
       apt-get install build-essential git-core checkinstall yasm texi2html libvorbis-dev libx11-dev libvpx-dev libxfixes-dev zlib1g-dev
       apt-get install pkg-config netcat libncurses5-dev
       apt-get install nasm libx264-dev
       apt-get install libv4l-dev
       apt-get install libasound2-dev
       apt-get install libsdl2-dev
       apt-get install libxext-dev

二、交叉编译asound

       先下载alsa-lib-1.2.1.2.tar.bz2解压缩。

      然后./configure CC=arm-linux-gnueabihf-gcc --host=arm-linux-gnueabihf 

              make

              make install

三、编译pjsip

        ./configure CC=arm-linux-gnueabihf-gcc --host=arm-linux-gnueabihf  --disable-libwebrtc --disable-ssl --disable-openh264 --disable-libyuv --enable-static --prefix=/system/bin

        make

       make install

四、使用pjsip静态库编译一个demo

       makefile如下

        

OBJ= main 

CC= arm-linux-gnueabihf-gcc 

CFLAGS= -Wall -O -O2 -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1
VPATH= ./
#LIBS= -lpthread -lm 

INCLUDE= -I$(VPATH)include
LIB_DIR= -L$(VPATH)lib 

LIB= -lpjsua2-arm-unknown-linux-gnueabihf -lstdc++ -lpjsua-arm-unknown-linux-gnueabihf -lpjsip-ua-arm-unknown-linux-gnueabihf -lpjsip-simple-arm-unknown-linux-gnueabihf -lpjsip-arm-unknown-linux-gnueabihf -lpjmedia-codec-arm-unknown-linux-gnueabihf -lpjmedia-arm-unknown-linux-gnueabihf -lpjmedia-videodev-arm-unknown-linux-gnueabihf -lpjmedia-audiodev-arm-unknown-linux-gnueabihf -lpjmedia-arm-unknown-linux-gnueabihf -lpjnath-arm-unknown-linux-gnueabihf -lpjlib-util-arm-unknown-linux-gnueabihf  -lsrtp-arm-unknown-linux-gnueabihf -lresample-arm-unknown-linux-gnueabihf -lgsmcodec-arm-unknown-linux-gnueabihf -lspeex-arm-unknown-linux-gnueabihf -lilbccodec-arm-unknown-linux-gnueabihf -lg7221codec-arm-unknown-linux-gnueabihf  -lpj-arm-unknown-linux-gnueabihf -lrt -lpthread  -lasound -ldl -lm

$(OBJ):
    $(CC) -o main.o main.c -Wall $(INCLUDE) $(LIB_DIR) $(LIB) -static 

clean:
    rm -f main 
#include "pj/log.h"
#include "pjlib.h"
#include "pjlib-util.h"
#include "pjnath.h"
#include "pjmedia.h"
#include "pjsip.h"
#include "pjsip_simple.h"
#include "pjsip_ua.h"
#include "pjsua-lib/pjsua.h"
#include "pjmedia-codec.h"

#define THIS_FILE "#### windaka_log"
#define SIP_DOMAIN "10.10.5.250"
#define SIP_USER   "01010011"
#define SIP_PASSWD "0000"

#define current_acc	pjsua_acc_get_default()

/*####获取所有声卡设备的结构####*/
typedef struct pjmedia_aud_sount_cnt{
    int devcnt;
    char name[32][32];
    unsigned devid[32];
}pjmedia_aud_sound_cnt;

/*####收到呼叫后的回调函数####*/
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);

	/*####打印出主叫的URL####*/
    PJ_LOG(3,(THIS_FILE, "#### ---->Incoming call from %.*s!!",
			 (int)ci.remote_info.slen,
			 ci.remote_info.ptr));

    /*####收到呼叫后的应答函数接口####*/
    pjsua_call_answer(call_id, 200, NULL, NULL);
}

/* 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,state=%d,last_status=%d", call_id,
			 (int)ci.state_text.slen,
			 ci.state_text.ptr,ci.state,ci.last_status));
}

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

/*####程序异常退出接口####*/
static void error_exit(const char *title, pj_status_t status)
{
    pjsua_perror(THIS_FILE, title, status);
    pjsua_destroy();
    exit(1);
}

void get_device_sound_name(char *pathName,char *sound_name){
    FILE *fp;
    char tmp[32];

    if(pathName == NULL){
        return ;
    }

    fp = fopen(pathName,"r");
    if(fp == NULL){
        return ;
    }

    memset(tmp,0,sizeof(tmp));
    fread(tmp,1,32,fp);
    strcpy(sound_name,tmp);
    fclose(fp);
    return ;
}

void del_str_line(char *str){
    char *p = str;

    while('\n' != *p){
        p++;
	if(*p =='\0'){
            return ;
	}
    }
    *p = '\0';
    return ;
}

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

    /*####初始化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);
        }
    }

    /*####设置pjsua使用的本地端口####*/
    {
        pjsua_transport_config cfg;

	pjsua_transport_config_default(&cfg);
	/*####本地使用5061端口####*/
	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);
        int iloop = 0;
        char devName[32];
        unsigned devId = 0;
	static pjmedia_aud_sound_cnt *p = NULL;

	/*####获取系统指定声卡的name####*/
	memset(devName,0,sizeof(devName));
        //get_device_sound_name("/proc/asound/card0/id",devName); 

	/*####获取设备所有的声卡信息####*/
        //printf("#### ---->get_card_calc_dev() \n");
        //p = get_card_calc_dev();
        //printf("#### ---->p->devcnt=[%d]\n",p->devcnt);

#if 0
	char tmp[64];
	memset(tmp,0,sizeof(tmp));
	strcpy(tmp,"default:CARD=");
	strcat(tmp,devName);
        del_str_line(tmp);

	/*####根据指定声卡的name获取声卡的ID####*/
	printf("#### ---->p->devcnt = [%d]\n",p->devcnt);
	for(iloop = 0; iloop < p->devcnt;iloop++){
            printf("#### ---->tmp=[%s],p->name[iloop]=[%s]\n",tmp,p->name[iloop]);
   	    if(strcmp(tmp,p->name[iloop]) == 0){
                devId = p->devid[iloop];
                printf("#### ---->get device [%s],id = [%d]\n",p->name[iloop],devId);
		break;
	    }
	}
	/*####指定设备使用哪个声卡####*/
	pjsua_set_snd_dev(devId,devId);
#endif
        /*####设置SIP账号信息####*/
        cfg.vid_out_auto_transmit = PJ_TRUE;
        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);

	/*####添加SIP账号####*/
        status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
        if (status != PJ_SUCCESS){
	    error_exit("Error adding account", status);
	}
    }

    /* Wait until user press "q" to quit. */
    for (;;) {
        char option[10];

        puts("\n'h' to hangup all calls\n'q' to quit\n'm' is call 17001010101\n'r' is register\n");
        if (fgets(option, sizeof(option), stdin) == NULL) {
            puts("EOF while reading stdin, will quit now..");
	    break;
	}

        if(option[0] == 'r'){
            pjsua_acc_set_registration(current_acc,PJ_TRUE);
        }

        if(option[0] == 'm'){
            pj_str_t uri=pj_str("sip:01010301@10.10.5.250");
            status = pjsua_call_make_call(acc_id,&uri,0,NULL,NULL,NULL);
            if(status != PJ_SUCCESS){
                printf("#### make call Error\n");
            }
        }
	if (option[0] == 'q'){
	    break;
	}

	if (option[0] == 'h'){
	    /*####挂断接口####*/
	    pjsua_call_hangup_all();
	}
    }

    /* Destroy pjsua */
    pjsua_destroy();

    return 0;
}


 

启动参数:
./pjsua-x86_64-unknown-linux-gnu --local-port=5061 --registrar=sip:10.10.7.19 --realm 10.10.7.19 --username 1003 --password 1003 --id sip:1003@10.10.7.19 --play-file=/home/jp/3.wav --auto-answer 200 --auto-play --auto-loop --null-audio --auto-rec --rec-file=xxxx  --auto-play-hangup

编译参数:
gcc  -g *.c pjsua_app.h -o main -Wall -I ./include -DPJ_AUTOCONF=1  -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1 -L ./lib -lpjsua2-x86_64-unknown-linux-gnu -lstdc++ -lpjsua-x86_64-unknown-linux-gnu -lpjsip-ua-x86_64-unknown-linux-gnu -lpjsip-simple-x86_64-unknown-linux-gnu -lpjsip-x86_64-unknown-linux-gnu -lpjmedia-codec-x86_64-unknown-linux-gnu -lpjmedia-videodev-x86_64-unknown-linux-gnu -lpjmedia-audiodev-x86_64-unknown-linux-gnu -lpjmedia-x86_64-unknown-linux-gnu -lpjnath-x86_64-unknown-linux-gnu -lpjlib-util-x86_64-unknown-linux-gnu -lpj-x86_64-unknown-linux-gnu -lsrtp-x86_64-unknown-linux-gnu -lresample-x86_64-unknown-linux-gnu -lgsmcodec-x86_64-unknown-linux-gnu -lspeex-x86_64-unknown-linux-gnu -lilbccodec-x86_64-unknown-linux-gnu -lg7221codec-x86_64-unknown-linux-gnu -lyuv-x86_64-unknown-linux-gnu -lwebrtc-x86_64-unknown-linux-gnu  -lssl -lcrypto -lm -lrt -lpthread  -lasound -lssl -lcrypto -lm -lrt -lpthread -g

        

      录音文件流程

wav_writer.c file_put_frame()

       ->file_io_ansi.c  pj_file_write()

                                       fwrite()

<think>我们正在讨论如何编译PJSIP的动态库。根据引用[4],在arm开发板上编译PJSIP并生成动态共享库时,使用了`--enable-shared`选项。同样,在引用[1]中,我们看到一个针对Android平台的配置示例,但其中没有明确提到动态库,不过我们可以推断类似的选项同样适用。 编译PJSIP动态库的一般步骤: 1. 配置环境:确保有交叉编译工具链(如果目标平台不是当前主机平台)和必要的依赖(如OpenH264等,根据需求)。 2. 运行配置脚本:使用`./configure`命令并指定`--enable-shared`来生成动态库。同时,可以指定安装路径(`--prefix`)和其他选项。 3. 编译:执行`make`命令。 4. 安装:执行`make install`命令,将库和头文件安装到指定目录。 以在Linux主机上编译动态库为例(如果是交叉编译,则需要设置相应的环境变量和工具链): 参考引用[4]中的命令: ``` ./configure --prefix=/opt/pjproject --enable-shared make make install ``` 如果是交叉编译(如Android),则需要使用类似引用[1]中的配置,并添加`--enable-shared`选项。例如: ``` NDK_TOOLCHAIN_VERSION=clang TARGET_ABI=arm64-v8a APP_PLATFORM=android-25 ./configure-android --use-ndk-cflags --with-openh264=/path/to/openh264 --enable-shared make make install ``` 注意:引用[1]中使用了`configure-android`,这是PJSIP提供的针对Android的配置脚本,位于PJSIP的根目录下。 另外,引用[2]中提到了交叉编译时使用`--disable-libwebrtc`来禁用某些不需要的功能,以及设置`CFLAGS`等。因此,根据实际需求,可能需要调整配置选项。 总结:编译动态库的关键是在配置时加上`--enable-shared`选项。 根据用户提供的引用信息,我们还可以看到: - 引用[3]提到了在Linux下使用gcc编译时链接动态库的方法(但那是使用库,不是编译库)。 - 引用[5]介绍了PJSIP的核心模块,但未涉及编译。 因此,我们重点参考引用[1]和引用[4]的配置方法。 下面给出一个更详细的步骤说明: 步骤1:下载PJSIP源代码(假设已经下载并进入源码目录)。 步骤2:配置编译选项。 - 对于非交叉编译(在主机上编译): ./configure --prefix=/your/install/path --enable-shared - 对于Android交叉编译(使用Android NDK): # 设置环境变量,如NDK路径(根据你的环境调整) export NDK_TOOLCHAIN_VERSION=clang export TARGET_ABI=arm64-v8a export APP_PLATFORM=android-25 ./configure-android --use-ndk-cflags --with-openh264=/path/to/openh264 --enable-shared 注意:openh264的路径需要替换为实际的路径,如果不需要openh264,可以去掉`--with-openh264`选项。 步骤3:编译 make dep make 步骤4:安装 make install 这样,在安装目录(由`--prefix`指定)的lib子目录下,应该会生成动态库文件(如.so文件)。 注意:在交叉编译时,可能需要先设置好交叉编译工具链的环境变量,或者通过配置脚本的参数指定。引用[1]中使用了配置脚本`configure-android`,它会自动处理Android的交叉编译设置。 如果遇到问题,可能需要检查依赖库是否已经正确编译并指定路径。 根据引用[3]的经验,在链接时可能需要链接其他库(如pthread),但编译库本身时,配置脚本通常会处理好这些依赖。 因此,用户的问题“如何编译pjSIP动态库”可以通过上述步骤完成。 生成相关问题: 1. 如何为Android平台交叉编译PJSIP动态库? 2. 编译PJSIP动态库时如何解决依赖问题(如OpenH264)? 3. PJSIP动态库在Linux下如何链接使用? 4. 编译PJSIP动态库时常见的错误及解决方法有哪些?</think>### 如何编译 PJSIP 动态库 编译 PJSIP 动态库的核心是启用 `--enable-shared` 配置选项,以下是具体步骤(以 Linux/Android 为例): #### 1. **基础编译流程** ```bash # 进入源码目录 cd pjproject # 配置编译选项(关键:--enable-shared) ./configure --prefix=/安装路径 --enable-shared # [^4] # 编译并安装 make dep make make install ``` - **动态库位置**:安装路径的 `lib/` 目录下生成 `.so` 文件(如 `libpjmedia.so`) - **关键选项**:`--enable-shared` 显式声明生成动态库 [^4] --- #### 2. **交叉编译场景(Android)** ```bash # 设置 Android NDK 环境变量 export NDK_TOOLCHAIN_VERSION=clang export TARGET_ABI=arm64-v8a export APP_PLATFORM=android-21 # 配置(支持 OpenH264 编解码) ./configure-android \ --use-ndk-cflags \ --with-openh264=/openh264路径 \ --enable-shared # [^1] ``` - **关键依赖**:需提前编译 OpenH264 的动态库 [^1] - **ABI 兼容**:修改 `TARGET_ABI` 可适配 `armeabi-v7a` 等架构 --- #### 3. **常见问题解决** - **依赖库缺失**: 编译时若提示 `undefined reference`,需显式链接依赖库(如多线程库): ```bash LDFLAGS="-lpthread -lm" ./configure --enable-shared # [^3] ``` - **静态库冲突**: 添加 `--disable-static` 确保只生成动态库: ```bash ./configure --enable-shared --disable-static ``` --- #### 4. **验证动态库** ```bash # 查看生成的 .so 文件 find /安装路径/lib -name "*.so" # 检查依赖项 ldd libpjsua2.so # 输出应包含 libopenh264.so 等 ``` > **重要提示**: > - 交叉编译需确保 **NDK 工具链版本**与目标平台匹配(如 `APP_PLATFORM=android-21`)[^1] > - 若需最小化体积,添加 `--disable-libwebrtc` 移除 WebRTC 支持 [^2] --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ware_soft

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值