软交换代码介绍


  1. 系统介绍

    1. 概述

 

ppx_softswitch系统是一套轻便级软交换系统,集成SIP呼叫管理,SIP用户管理,媒体处理等功能,面向企业通讯,呼叫中心等行业提供基础通讯服务,IPPBX业务功能等。



    1. 设计概述

ppx_softswitch采用C语言,基于组件化的设计思想来开发。运行在Linux平台上。


    1. 代码目录介绍

      1. 总目录

build系统makefile存放路径

conf软件原始配置文件存放路径

docs:介绍文档存放路径

libs:软件使用的库源代码存放目录

src:功能模块源代码存放目录

target:编译产生的目标文件和临时库文件存放目录

build_beta_ver.sh:生成版本信息

build_ver.sh生成版本信息

env.mk:设置编译时环境变量等

Makefile:系统入口makefile


      1. 依赖库目录

libedit命令行交互命令CLI基础库源代码

sofia-sipNokia开发的SIP开源协议栈,负责SIP消息解析和封装,SIP事物管理,SDP消息解析和封装等

Makefile编译库源代码的入口makefile文件


      1. 功能模块目录

codes系统支持的编码源文件存放路径

core系统入口源文件存放路径,负责配置文件的加载,所有模块的初始化操作等

formats语音文件的读取和写入源代码存放路径

include公共头文件存放路径

media_streamer媒体处理相关源代码存放路径,包括rtp协议解析,封装,RTP会话管理,RFC233 DTMF检测处理,音频挂钩(录音和会议等应用)处理。

meeting会议处理单元源代码存放路径

pbxPBX业务功能处理源代码存放路径,包括呼叫对话管理等。

scm:SIP注册管理,SIP通道管理等源代码存放路径

synway_adapter三汇适配API服务端源代码存放路径

utils通用函数库源代码存放路径

Makefile编译业务功能模块的入口makefile,会自动递归调用src目录的业务功能模块


      1. 业务模块编译Makefile介绍

负责编译所有src目录的业务功能模块,采用递归自动扫描src目录的makefile文件,然后进行编译。

INCLUDES = \

        -I$(MAKEROOT)/target/include/sofia-sip-1.12 \

        -I$(MAKEROOT)/libs/sofia-sip/libsofia-sip-ua/nua \

        -I$(LIB_DIR)/libedit \

        -I$(MAKEROOT)/src/pbx \

        -I$(MAKEROOT)/src/media_streamer \

        -I$(MAKEROOT)/src/synway_adapter \

        -I$(MAKEROOT)/src/include \

        -I$(MAKEROOT)/src/core \

        -I$(MAKEROOT)/src/scm \

        -I$(MAKEROOT)/src/formats \

        -I$(MAKEROOT)/src/codes \

        -I$(MAKEROOT)/src/meeting \

        -I$(MAKEROOT)/src/utils

export INCLUDES

SUBS := $(shell find . -name "Makefile")

SUBDIRS := $(dir $(filter-out ./Makefile, $(shell find . -name "Makefile")))

include $(MAKEROOT)/evn.mk

define build_sub_obj

        for dir in $(SUBDIRS); do \

                make -C $$dir; \

        done

endef

.PHONY: all

all:

        @echo "Buildng all applications start..."

        @$(call build_sub_obj)

        $(CC) $(TARGET_DIR)/*.o $(TARGET_DIR)/slibs/*.a $(TARGET_DIR)/lib/libedit.a $(LDFLAGS) -o $(TARGET_PROG)


  1. Sofia-SIP应用开发介绍

    1. Sofia-SIP介绍

Sofia-SIP is an open-source SIP User-Agent library, compliant with the IETF RFC3261 specification. It can be used as a building block for SIP client software for uses such as VoIP, IM, and many other real-time and person-to-person communication services. The primary target platform for Sofia-SIP is GNU/Linux. Sofia-SIP is based on a SIP stack developed at the Nokia Research Center. Sofia-SIP is licensed under the LGPL.


    1. Sofia-SIP代码目录结构

In libsofia-sip-ua, there are subdirectories for different modules listed below.

Terminal and high-level libraries used for both signaling and media:

Common runtime library:

SIP Signaling:

HTTP subsystem:

SDP processing:

Other:

Features provided by Sofia-SIP library:

Documentation:

  • "docs" - Doxygen reference documentation

    1. 基于Sofia-SIP开发switch简介

Sofia-SIP内部使用消息队列对SIP消息进行处理,和应用层直接采用回调函数方式进行通信。


      1. Sofia-SIP库初始化

Sofia-SIP库在使用之前,必须进行初始化Sofia-SIP,分配空间和初始化环境变量。

static ppx_int32_t sofia_lib_init()

{

ppx_int32_t level = 0;

su_init();

if (sip_update_default_mclass(sip_extend_mclass(NULL)) < 0)

{

su_deinit();

return -1;

}

/* Redirect out log in sofia */

su_log_redirect(su_log_default, sofia_redirect_log, NULL);

su_log_redirect(tport_log, sofia_redirect_log, NULL);

su_log_redirect(iptsec_log, sofia_redirect_log, NULL);

su_log_redirect(nea_log, sofia_redirect_log, NULL);

su_log_redirect(nta_log, sofia_redirect_log, NULL);

su_log_redirect(nth_client_log, sofia_redirect_log, NULL);

su_log_redirect(nth_server_log, sofia_redirect_log, NULL);

su_log_redirect(nua_log, sofia_redirect_log, NULL);

su_log_redirect(soa_log, sofia_redirect_log, NULL);

su_log_redirect(sresolv_log, sofia_redirect_log, NULL);

sip_set_loglevel("all", level);

return 0;

}


      1. 创建一个UserAgent

对每对IP和端口,我们都应该创建一个UserAgent来处理SIP呼叫请求,同时将处理呼叫事件的回调函数注册到Sofia-SIP库中。

static void * sip_profile_run(void *argv)

{

sip_profile_t *sip_profile =  (sip_profile_t *)argv;;

call_msg_t *call_msg = NULL;

sip_profile->root = su_root_create(NULL);

sip_profile->home = su_home_new(sizeof(*sip_profile->home));

sip_profile->nua = nua_create(sip_profile->root,

sip_channels_event_callback,

sip_profile,

NUTAG_URL(sip_profile->bind_url),

NTATAG_USER_VIA(1),

TAG_IF(!strchr(sip_profile->sip_ip, ':'), SOATAG_AF(SOA_AF_IP4_ONLY)),

TAG_IF(strchr(sip_profile->sip_ip, ':'), SOATAG_AF(SOA_AF_IP6_ONLY)),

TAG_IF(!strchr(sip_profile->sip_ip, ':'), NTATAG_UDP_MTU(65535)),

TAG_IF(0, NTATAG_USE_SRV(0)),

TAG_IF(0, NTATAG_USE_NAPTR(0)),

NTATAG_DEFAULT_PROXY(sip_profile->outbound_proxy),

NTATAG_SERVER_RPORT(sip_profile->server_rport_level),

NTATAG_CLIENT_RPORT(sip_profile->client_rport_level),

NTATAG_SERVER_RPORT(sip_profile->rport_level),

TPTAG_LOG(sip_profile->sip_trace),

TAG_IF(0, NTATAG_SIPFLAGS(MSG_DO_COMPACT)),

TAG_IF(sip_profile->timer_t1, NTATAG_SIP_T1(sip_profile->timer_t1)),

TAG_IF(sip_profile->timer_t1x64, NTATAG_SIP_T1X64(sip_profile->timer_t1x64)),

TAG_IF(sip_profile->timer_t2, NTATAG_SIP_T2(sip_profile->timer_t2)),

TAG_IF(sip_profile->timer_t4, NTATAG_SIP_T4(sip_profile->timer_t4)),

SIPTAG_ACCEPT_STR("application/sdp"),

TAG_END());

if (!sip_profile->nua)

{

ppx_log(PPX_LOG_ERROR, "Create sip profile %s (%s:%d) failed\n", sip_profile->name, sip_profile->sip_ip, sip_profile->sip_port);

sip_profile->state = SP_ST_DESDROY;

return NULL;

}

nua_set_params(sip_profile->nua,

SIPTAG_ALLOW_STR("INVITE, ACK, BYE, CANCEL, OPTIONS, INFO"),

NUTAG_ALLOW("REGISTER"),

NUTAG_APPL_METHOD("REGISTER"),

NUTAG_APPL_METHOD("OPTIONS"),

NUTAG_APPL_METHOD("INFO"),

NUTAG_APPL_METHOD("ACK"),

NUTAG_APPL_METHOD("BYE"),

NUTAG_AUTOANSWER(0),

NUTAG_AUTOACK(0),

NUTAG_AUTOALERT(0),

TAG_IF(0, NUTAG_ALLOW("PRACK")),

NUTAG_ALLOW("INFO"),

NUTAG_SESSION_TIMER(sip_profile->session_timeout),

NTATAG_MAX_PROCEEDING(sip_profile->max_proceeding),

SIPTAG_SUPPORTED_STR(NULL/*supported*/),

SIPTAG_USER_AGENT_STR(g_sofia_ctrl->user_agent),

TAG_END());

ppx_log(PPX_LOG_NOTICE, "********SIP Profile '%s' Running ON %s:%d********\n", sip_profile->name, sip_profile->sip_ip, sip_profile->sip_port);

sip_profile->state = SP_ST_ACTIVE;

while (sip_profile->state < SP_ST_STOP)

{

call_msg = sip_profile_call_msg_pop(sip_profile);

if (call_msg)

{

sip_call_msg_proc(sip_profile, call_msg);

call_msg_free(call_msg);

su_root_step(sip_profile->root, 0);

}

else

{

su_root_step(sip_profile->root, 10);

}

}

ppx_log(PPX_LOG_WARNING, "Stop sip profile %s\n", sip_profile->name);

nua_shutdown(sip_profile->nua);

su_root_run(sip_profile->root);

nua_destroy(sip_profile->nua);

su_home_unref(sip_profile->home);

su_root_destroy(sip_profile->root);

sip_profile->state = SP_ST_DESDROY;

return NULL;

}


      1. Sofia-SIP呼叫事件处理函数

应用层将回调函数注册到Soifa-SIP库中,Soifa-SIP协议栈解析SIP消息后,再进行SIP事务处理,根据不同状态和SIP消息,产生相应的呼叫事件消息,然后在回到应用层注册的回调函数。

static void sip_channels_event_callback(nua_event_t msg, ppx_int32_t status, char const *phrase, nua_t *nua, nua_magic_t *profile_magic,

nua_handle_t *nh, nua_hmagic_t *sofia_private_magic, sip_t const *sip, tagi_t tags[])

{

ppx_int32_t check_destroy = 1;

sip_channel_t *chan = NULL;

sip_profile_t *profile = (sip_profile_t *)profile_magic;

sofia_private_t *sofia_private = (sofia_private_t *)sofia_private_magic;

ppx_log(PPX_LOG_DEBUG, "sip msg(%s) status(%d %s) uuid:%s\n", nua_event_name(msg), status, VSTR(phrase), (sofia_private && '\0' != sofia_private->uuid[0]) ? sofia_private->uuid : "");

if (sofia_private)

{

if ('\0' != sofia_private->uuid[0])

{

if ((chan = sip_chan_find(sofia_private->uuid)))

{

if (!chan->call_id && sip && sip->sip_call_id && sip->sip_call_id->i_id)

{

chan->call_id = sip_chan_strdup(chan, (char *)sip->sip_call_id->i_id);

}

if (chan->state >= SCST_WAIT_DESTROY)

{

ppx_log(PPX_LOG_DEBUG, "The sip chan %s is already hungup, waiting for destroy\n", chan->uuid_str);

goto done;

}

}

else

{

ppx_log(PPX_LOG_DEBUG, "sip chan %s not found\n", sofia_private->uuid);

return;

}

}

}

switch(msg)

{

case nua_i_state:

handle_sip_i_state(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags);

break;

case nua_r_get_params:

case nua_i_fork:

case nua_r_info:

break;

case nua_r_bye:

case nua_r_unregister:

case nua_r_unsubscribe:

case nua_r_publish:

break;

case nua_r_cancel:

if (status > 299)

{

if (nh)

{

if (sofia_private)

{

nua_handle_bind(nh, NULL);

}

nua_handle_destroy(nh);

nh = NULL;

}

if (chan)

{

if (sofia_private)

{

ppx_safe_free(sofia_private);

chan->sofia_private = NULL;

}

chan->state = SCST_WAIT_DESTROY;

}

}

break;

case nua_i_error:

break;

case nua_i_active:

case nua_r_set_params:

case nua_i_prack:

case nua_r_prack:

break;

case nua_i_cancel:

if (chan)

{

chan->recv_cancel = PPX_TRUE;

}

break;

case nua_i_terminated:

if (chan)

{

chan->state = SCST_WAIT_DESTROY;

}

break;

case nua_i_ack:

if (chan && sip)

{

if (PPX_FALSE == chan->reinvite)

{

if (!chan->call_id && sip->sip_call_id && sip->sip_call_id->i_id)

{

chan->call_id = sip_chan_strdup(chan, (char *)sip->sip_call_id->i_id);

}

extract_sip_header_vars(profile, sip, chan);

}

}

case nua_r_ack:

break;

case nua_r_shutdown:

if (status >= 200)

{

su_root_break(profile->root);

}

break;

case nua_r_message:

handle_sip_r_message(status, profile, nh, sip);

break;

case nua_r_invite:

if (sip && status >= 400 && sip->sip_reason && sip->sip_reason->re_protocol && (!strcasecmp(sip->sip_reason->re_protocol, "Q.850")) && sip->sip_reason->re_cause)

{

chan->q850_cause = atoi(sip->sip_reason->re_cause);

}

if (status == 407 && sip)

{

if (PPX_TRUE != handle_sip_r_challenge(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags))

{

if (chan)

{

if (SCST_PROGRESS >= chan->state)

{

handle_sip_r_456xx_response(chan, 503, NULL);

}

else if (SCST_ACTIVE == chan->state)

{

sip_channel_build_bye_msg(chan, 0);

}

chan->nh = NULL;

chan->state = SCST_WAIT_DESTROY;

chan->sofia_private = NULL;

}

if (sofia_private)

{

sofia_private->destroy_me = 1;

sofia_private->destroy_nh = 1;

}

}

}

else

{

sofia_handle_sip_r_invite(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags);

}

break;

case nua_r_options:

break;

case nua_i_bye:

handle_sip_i_bye(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags);

break;

case nua_r_notify:

handle_sip_r_notify(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags);

break;

case nua_i_notify:

handle_sip_i_notify(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags);

break;

case nua_r_register:

if (sip)

{

if (status == 401)

{

handle_sip_r_challenge(chan, status, phrase, nua, profile, nh, sofia_private, sip, tags);

}

else

{

handle_sip_r_register(status, phrase, nua, profile, nh, sofia_private, sip, tags);

}

}

break;

case nua_i_options:

handle_sip_i_options(status, phrase, nua, profile, nh, sofia_private, sip, tags);

break;

case nua_i_invite:

if (NULL == chan)

{

handle_sip_i_invite(nua, profile, nh, sip, tags);

}

else

{

chan->reinvite = PPX_TRUE;

handle_sip_i_reinvite(chan, nua, profile, nh, sofia_private, sip, tags);

}

break;

case nua_i_publish:

ppx_log(PPX_LOG_INFO, "%s", "Received PUBLISH request, wo don't handle it\n");

break;

case nua_i_register:

handle_sip_i_register(nua, profile, nh, sip, PPX_TRUE, NULL, 0, tags);

break;

case nua_i_message:

ppx_log(PPX_LOG_INFO, "%s", "Received MESSAGE request, wo don't handle it\n");

break;

case nua_i_info:

handle_sip_i_info(nua, profile, nh, chan, sip, tags);

break;

case nua_i_update:

break;

case nua_r_update:

break;

case nua_r_refer:

break;

case nua_i_refer:

ppx_log(PPX_LOG_INFO, "%s", "Received REFER request, wo don't handle it\n");

break;

case nua_r_subscribe:

ppx_log(PPX_LOG_INFO, "%s", "Received SUBSCRIBE response, wo don't handle it\n");

break;

case nua_i_subscribe:

ppx_log(PPX_LOG_INFO, "%s", "Received SUBSCRIBE request, wo don't handle it\n");

break;

case nua_r_authenticate:

if (status >= 500)

{

if (sofia_private && '\0' !=sofia_private->reg_id[0])

{

ppx_sip_gateway_update(sofia_private->reg_id, 0, status);

}

else

{

nua_handle_destroy(nh);

}

}

break;

default:

if (status > 100)

{

ppx_log(PPX_LOG_INFO, "%s: unknown msg %d: %03d %s\n", nua_event_name(msg), msg, status, phrase);

}

else

{

ppx_log(PPX_LOG_INFO, "%s: unknown msg %d\n", nua_event_name(msg), msg);

}

break;

}

done:

switch (msg)

{

case nua_i_subscribe:

case nua_r_notify:

check_destroy = 0;

break;

case nua_i_notify:

if (sip && sip->sip_event && !strcmp(sip->sip_event->o_type, "dialog") && sip->sip_event->o_params && !strcmp(sip->sip_event->o_params[0], "sla"))

{

check_destroy = 0;

}

break;

default:

;

}

if (check_destroy)

{

if (nh && ((sofia_private && sofia_private->destroy_nh) || !nua_handle_magic(nh)))

{

if (sofia_private)

{

nua_handle_bind(nh, NULL);

}

nua_handle_destroy(nh);

nh = NULL;

}

}

if (sofia_private && sofia_private->destroy_me)

{

if (chan)

{

chan->sofia_private = NULL;

}

if (nh)

{

nua_handle_bind(nh, NULL);

}

ppx_safe_free(sofia_private);

}

if (chan && SCST_WAIT_DESTROY == chan->state)

{

sip_chan_free(&chan);

}

}


  1. 源文件概述

业务层模块如下图所示:

下面对业务层的文件实现功能进行简单的描述和介绍


    1. Codes模块

ppx_alaw.c    G.711a编码和解码实现源文件

ppx_alaw.h    G.711a编解码函数声明头文件

ppx_codec.c   Codec模块初始化,释放等函数实现源文件

ppx_codec.h   Codec模块初始化,释放等函数声明头文件

ppx_ulaw.c    G.711u编码和解码实现源文件

ppx_ulaw.h    G.711u编解码函数声明头文件


    1. Core模块

cli_client.c    CLI客户端功能实现源文件,ppx_softswitch –c时使用,即启动CLI客户端连接ppx_softswitch后台程序,然后可以执行ppx_softswitch的CLI命令,包括修改日志级别,查询当前呼叫通道等命令。

cli_client.h    CLI客户端模块初始化,启动,退出和释放函数声明头文件

cli_server.c    CLI服务端功能实现源文件,主要包括TCP服务端实现,CLI命令接收,解析,分发和处理等。

cli_server.h    CLI服务端模块初始化,启动,退出,释放和CLI命令注册等函数声明头文件

main.c    程序启动入口等函数实现源文件,包括main函数,唯一启动功能实现等

ppx_core.c    系统核心模块初始化,启动,退出,释放函数,配置文件读取等函数实现源文件,所有子模块的初始化,启动都在此文件封装的函数中调用

ppx_core.h   系统核心模块初始化,启动,退出和释放函数声明头文件


    1. Formats模块

ppx_format_file.h    格式化音频文件读写声明头文件

ppx_format_wav.c    wav格式音频文件读写函数实现源文件

ppx_voice_file.c    格式化音频文件读写模块初始化,启动等函数实现源文件,也包括预加载语言文件管理等函数的实现

ppx_voice_file.h    格式化音频文件读写模块初始化,启动等函数声明头文件


    1. Include目录

ppx_define.h    系统定义常量

ppx_preinclude.h    系统需要包括头文件

version.h    编译时生成的版本信息文件,由Makefile时自动生成


    1. Media_Streamer模块

ppx_audiohook.c    音频媒体帧钩子实现源文件,给录音和会议等应用提供音频媒体帧

ppx_audiohook.h    音频媒体帧钩子声明头文件。

rtp.c    rtp协议实现源文件

rtp.h    rtp函数声明头文件

rtp_port_allocator.c    rtp端口分配和管理实现源文件

rtp_port_allocator.h    rtp端口分配和管理声明头文件

rtp_rfc2833.c    RFC2833实现源文件

rtp_session.c    rtp会话管理实现源文件,包括rtp媒体流的接收,中转等

rtp_session.h    rtp会话管理函数声明头文件


    1. Meeting模块

ppx_meeting.c    会议模块实现源文件,包括会议的加入,混音处理等

ppx_meeting.h    会议模块函数声明头文件


    1. PBX模块

call_msg.c    模块内部呼叫消息封装和处理实现源文件

call_msg.h    模块内部消息声明头文件

call_session.c    呼叫业务处理实现源文件,比如呼叫接续,桥接,挂掉,播放语言等业务功能

call_session.h    呼叫业务处理函数声明头文件

routing.c    呼叫路由查询实现源文件

routing.h    呼叫路由查询函数声明头文件


    1. SCM模块

sip_chan.c   SIP呼叫管理实现源文件,包括sip通道的申请,查询和维护等

sip_chan.h   SIP呼叫管理函数声明头文件

sip_register.c    SIP注册管理实现模块,包括SIP用户注册,向网关发送注册请求等的实现

sip_register.h    SIP注册管理模块函数声明头文件

sip_sofia.c    Sofia-SIP协议栈封装处理模块,处理SIP呼叫,转换为内部呼叫消息,然在在分发给呼叫处理层进行处理。SIP Profile文件的加载和管理等。

sip_sofia.h    Sofia-SIP协议栈封装处理模块函数声明头文件。


    1. Synway_Adapter模块

pbx_adapter.c    业务层调用的动态库libppx_adapter.so对应的服务端实现模块,包括TCP服务端实现,适配三汇API内部呼叫消息的收发,解析和处理等

pbx_adapter.h   适配三汇API模块函数声明头文件

    1.  Utils模块

ppx_hash.c    哈希表实现源文件

ppx_hash.h    哈希表函数声明头文件

ppx_log.c    日志实现源文件,包括写日志,日志轮询等实现。

ppx_log.h    日志处理函数声明头文件

ppx_md5.c   MD5算法实现源文件

ppx_md5.h   MD5算法函数声明头文件

ppx_socket.c  Linux Socket封装实现源文件

ppx_socket.h  Linux Socket封装函数声明头文件

ppx_type.h   系统内部变量类型定义

ppx_utils.c    常用函数实现源文件,包括

ppx_utils.h    常用函数声明头文件


  1. 呼叫流程介绍

    1. 呼入流程介绍

    1. 呼出流程介绍

    1. 挂机流程介绍

    1. 彩铃播放流程介绍

    1. 电话会议流程介绍


    1. PBX呼入状态迁移介绍

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TOMSER123

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

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

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

打赏作者

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

抵扣说明:

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

余额充值