前面研究了libpjsip库的媒体层的各个模块, 从今天开始进入信令层的研究
从何处入手呢?还是从libpjsip wiki上找找思路吧!
借用下http://www.pjsip.org/docs/latest/pjsip/docs/html/index.htm 这张图
从下往上看,libpjlib位于最底层,供信令层和媒体层共用, 这个应该是做操作系统层的封装, 我们研究目标是信令层,所以不用管它,很明显,我们的重点目标应该是 PJSIP-CORE这一层, 下面摘录其中对各个层的描述:
Enumerating the static libraries from the bottom:
PJLIB, is the platform abstraction and framework library, on which all other libraries depend,
PJLIB-UTIL, provides auxiliary functions such as text scanning, XML, and STUN,
PJMEDIA is the multimedia framework,
PJMEDIA-CODEC is the placeholder for media codecs,
Core SIP Library (PJSIP-CORE) is the very core of the PJSIP library, and contains the SIP Endpoint, which is the owner/manager for all SIP objects in the application, messaging elements, parsing, transport management, module management, and stateless operations, and also contains:
The Transaction Layer module inside PJSIP-CORE provides stateful operation, and is the base for higher layer features such as dialogs,
The Base User Agent Layer/Common Dialog Layer module inside PJSIP-CORE manages dialogs, and supports dialog usages,
Event and Presence Framework (PJSIP-SIMPLE) provides the base SIP event framework (which uses the common/base dialog framework) and implements presence on top of it, and is also used by call transfer functions,
User Agent Library (PJSIP-UA) is the high level abstraction of INVITE sessions (using the common/base dialog framework). This library also provides SIP client registration and call transfer functionality,
and finally, PJSUA API - High Level Softphone API (PJSUA-LIB) is the highest level of abstraction, which wraps together all above functionalities into high level, easy to use API.
我们最关心的SIP事务层和SIP用户代理层/SIP会话层都位于PJSIP-CORE中, PJSIP-SIMPLE好像提供与VOIP电话INVITE事务无关的一些事件的处理框架。
在媒体层,我们最后才学习到 MEDIA Endpoint,它实现了声音系统和编解码器的初始化, 并为媒体数据的“流动”提供了驱动力
这回学习信令层,我们反过来试试, 先来研究一下SIP Endpoint,参考这篇文章:http://www.cnblogs.com/rayfloyd/p/7206815.html
借用一下里面的配图, SIP Endpoint也处于系统的核心,我将按自下往上的顺序,学习SIP的主要模块
==============================================================================================================================================================
一) pjsip_endpoint的定义
struct pjsip_endpoint
{
pj_pool_t *pool; /** Pool to allocate memory for the endpoint. */
pj_mutex_t *mutex; /** Mutex for the pool, hash table, and event list/queue. */
pj_pool_factory *pf; /** Pool factory. */
pj_str_t name; /** Name. */
pj_timer_heap_t *timer_heap; /** Timer heap. */
pjsip_tpmgr *transport_mgr; /** Transport manager. */
pj_ioqueue_t *ioqueue; /** Ioqueue. */
pj_status_t ioq_last_err; /** Last ioqueue err */
pjsip_resolver_t *resolver; /** DNS Resolver. */
pj_rwmutex_t *mod_mutex; /** Modules lock. */
pjsip_module *modules[PJSIP_MAX_MODULE]; /** Modules. */
pjsip_module module_list; /** Module list, sorted by priority. */
pjsip_hdr cap_hdr; /** Capability header list. */
pjsip_hdr req_hdr; /** Additional request headers. */
exit_cb exit_cb_list; /** List of exit callback. */
};
先粗略看一下,结构成员, 从字面上可以猜到的成员函数相关的功能如下:
1) ioqueue 有了MEDIA Endpoint的经验, 这个应该是与SIP收发网络套接子轮询有关
2) timer_heap 这个应该与SIP协议实现的各种定时器有关
3) transport_mgr 这个应该与传输层的管理有关,是我们在pjsip_endpoint后将要研究的目标
4) mod_mutex
modules[] 这个三个应该与SIP消息的处理有关
module_list
5) req_hdr 这个可能与SIP请求消息的头部相关
6) req_hdr 这个可能与SIP请求消息的头部的头域相关
二) pjsip_endpoint的创建函数,有了MEDIA Endpoint的经验, 这个函数应该是做了一系列的初始化动作,特别是启动了网络层和SIP信令处理栈
现在我们还不清楚SIP信令处理栈的工作机制,只是从一)中的分析来看, 应该与mod_xxx成员由关系
为了方便,下面摘录的初始化函数中,我把代码中一些判断代码和日志相关代码省略了。
PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, const char *name, pjsip_endpoint **p_endpt)
{
。。。
endpt = PJ_POOL_ZALLOC_T(pool, pjsip_endpoint); /* Create endpoint. */
。。。
pj_list_init(&endpt->module_list); /* Init modules list. */
pj_list_init(&endpt->exit_cb_list); /* Initialize exit callback list. */
。。。
pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex); /* Create R/W mutex for module manipulation. */
。。。
init_sip_parser(); /* Init parser. */
pjsip_tel_uri_subsys_init(); /* Init tel: uri */
。。。
pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex ); /* Create mutex for the events, etc. */
。。。
/* Create timer heap to manage all timers within this endpoint. */
pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, &endpt->timer_heap);
/* Set recursive lock for the timer heap. */
pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);。
pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
/* Set maximum timed out entries to process in a single poll. */
pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap, PJSIP_MAX_TIMED_OUT_ENTRIES);
。。。
pj_ioqueue_create(endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); /* Create ioqueue. */
。。。
/* Create transport manager. */
pjsip_tpmgr_create( endpt->pool, endpt, &endpt_on_rx_msg, &endpt_on_tx_msg, &endpt->transport_mgr);
。。。
pjsip_resolver_create(endpt->pool, &endpt->resolver); /* Create asynchronous DNS resolver. */
。。。
pj_list_init(&endpt->req_hdr); /* Initialize request headers. */
mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool, PJSIP_MAX_FORWARDS_VALUE); /* Add "Max-Forwards" for request header. */
pj_list_insert_before( &endpt->req_hdr, mf_hdr);
。。。
pj_list_init(&endpt->cap_hdr);/* Initialize capability header list. */
/* Done. */
*p_endpt = endpt;
return status;
}
重点关注一下与传输层有关的两句:
1) pj_ioqueue_create(endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); /* Create ioqueue. */
2) pjsip_tpmgr_create( endpt->pool, endpt, &endpt_on_rx_msg, &endpt_on_tx_msg, &endpt->transport_mgr);
问题: endpt->ioqueue是在哪里及实现轮询让SIP信息流动起来的呢?
在pjsua_core.c中,可以看到使用工作线程进行SIP消息端口轮询的例子 :
pjsua_init()会启动如下的工作线程, pjsua_handle_events调用pjsip_endpt_handle_events2()实现对endpt->ioqueue的轮询
static int worker_thread(void *arg)
{
enum { TIMEOUT = 10 };
PJ_UNUSED_ARG(arg);
while (!pjsua_var.thread_quit_flag) {
int count;
count = pjsua_handle_events(TIMEOUT);
if (count < 0)
pj_thread_sleep(TIMEOUT);
}
return 0;
}
另外,在simpleua.c的main函数中, 也看到使用主任务实现SIP消息端口轮询的例子:
for (;!g_complete;) {
pj_time_val timeout = {0, 10};
pjsip_endpt_handle_events(g_endpt, &timeout);
}
至此, SIP被驱动起来了!明天研究下传输层及网络及其抽象层pjsip_transport:
struct udp_transport
{
pjsip_transport base;
pj_sock_t sock;
pj_ioqueue_key_t *key;
int rdata_cnt;
pjsip_rx_data **rdata;
int is_closing;
pj_bool_t is_paused;
int read_loop_spin;
/* Group lock to be used by UDP transport and ioqueue key */
pj_grp_lock_t *grp_lock;
};
struct pjsip_transport
{
char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
pj_pool_t *pool; /**< Pool used by transport. */
pj_atomic_t *ref_cnt; /**< Reference counter. */
pj_lock_t *lock; /**< Lock object. */
pj_bool_t tracing; /**< Tracing enabled? */
pj_bool_t is_shutdown; /**< Being shutdown? */
pj_bool_t is_destroying; /**< Destroy in progress? */
/** Key for indexing this transport in hash table. */
pjsip_transport_key key;
char *type_name; /**< Type name. */
unsigned flag; /**< #pjsip_transport_flags_e */
char *info; /**< Transport info/description.*/
int addr_len; /**< Length of addresses. */
pj_sockaddr local_addr; /**< Bound address. */
pjsip_host_port local_name; /**< Published name (eg. STUN). */
pjsip_host_port remote_name; /**< Remote address name. */
pjsip_transport_dir dir; /**< Connection direction. */
pjsip_endpoint *endpt; /**< Endpoint instance. */
pjsip_tpmgr *tpmgr; /**< Transport manager. */
pjsip_tpfactory *factory; /**< Factory instance. Note: it
may be invalid/shutdown. */
pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/
pj_timestamp last_recv_ts; /**< Last time receiving data. */
pj_size_t last_recv_len; /**< Last received data length. */
void *data; /**< Internal transport data. */
/**
* Function to be called by transport manager to send SIP message.
*
* @param transport The transport to send the message.
* @param packet The buffer to send.
* @param length The length of the buffer to send.
* @param op_key Completion token, which will be supplied to
* caller when pending send operation completes.
* @param rem_addr The remote destination address.
* @param addr_len Size of remote address.
* @param callback If supplied, the callback will be called
* once a pending transmission has completed. If
* the function completes immediately (i.e. return
* code is not PJ_EPENDING), the callback will not
* be called.
*
* @return Should return PJ_SUCCESS only if data has been
* succesfully queued to operating system for
* transmission. Otherwise it may return PJ_EPENDING
* if the underlying transport can not send the
* data immediately and will send it later, which in
* this case caller doesn't have to do anything
* except wait the calback to be called, if it
* supplies one.
* Other return values indicate the error code.
*/
pj_status_t (*send_msg)(pjsip_transport *transport,
pjsip_tx_data *tdata,
const pj_sockaddr_t *rem_addr,
int addr_len,
void *token,
pjsip_transport_callback callback);
/**
* Instruct the transport to initiate graceful shutdown procedure.
* After all objects release their reference to this transport,
* the transport will be deleted.
*
* Note that application MUST use #pjsip_transport_shutdown() instead.
*
* @param transport The transport.
*
* @return PJ_SUCCESS on success.
*/
pj_status_t (*do_shutdown)(pjsip_transport *transport);
/**
* Forcefully destroy this transport regardless whether there are
* objects that currently use this transport. This function should only
* be called by transport manager or other internal objects (such as the
* transport itself) who know what they're doing. Application should use
* #pjsip_transport_shutdown() instead.
*
* @param transport The transport.
*
* @return PJ_SUCCESS on success.
*/
pj_status_t (*destroy)(pjsip_transport *transport);
/*
* Application may extend this structure..
*/
};
从何处入手呢?还是从libpjsip wiki上找找思路吧!
借用下http://www.pjsip.org/docs/latest/pjsip/docs/html/index.htm 这张图
从下往上看,libpjlib位于最底层,供信令层和媒体层共用, 这个应该是做操作系统层的封装, 我们研究目标是信令层,所以不用管它,很明显,我们的重点目标应该是 PJSIP-CORE这一层, 下面摘录其中对各个层的描述:
Enumerating the static libraries from the bottom:
PJLIB, is the platform abstraction and framework library, on which all other libraries depend,
PJLIB-UTIL, provides auxiliary functions such as text scanning, XML, and STUN,
PJMEDIA is the multimedia framework,
PJMEDIA-CODEC is the placeholder for media codecs,
Core SIP Library (PJSIP-CORE) is the very core of the PJSIP library, and contains the SIP Endpoint, which is the owner/manager for all SIP objects in the application, messaging elements, parsing, transport management, module management, and stateless operations, and also contains:
The Transaction Layer module inside PJSIP-CORE provides stateful operation, and is the base for higher layer features such as dialogs,
The Base User Agent Layer/Common Dialog Layer module inside PJSIP-CORE manages dialogs, and supports dialog usages,
Event and Presence Framework (PJSIP-SIMPLE) provides the base SIP event framework (which uses the common/base dialog framework) and implements presence on top of it, and is also used by call transfer functions,
User Agent Library (PJSIP-UA) is the high level abstraction of INVITE sessions (using the common/base dialog framework). This library also provides SIP client registration and call transfer functionality,
and finally, PJSUA API - High Level Softphone API (PJSUA-LIB) is the highest level of abstraction, which wraps together all above functionalities into high level, easy to use API.
我们最关心的SIP事务层和SIP用户代理层/SIP会话层都位于PJSIP-CORE中, PJSIP-SIMPLE好像提供与VOIP电话INVITE事务无关的一些事件的处理框架。
在媒体层,我们最后才学习到 MEDIA Endpoint,它实现了声音系统和编解码器的初始化, 并为媒体数据的“流动”提供了驱动力
这回学习信令层,我们反过来试试, 先来研究一下SIP Endpoint,参考这篇文章:http://www.cnblogs.com/rayfloyd/p/7206815.html
借用一下里面的配图, SIP Endpoint也处于系统的核心,我将按自下往上的顺序,学习SIP的主要模块
==============================================================================================================================================================
一) pjsip_endpoint的定义
struct pjsip_endpoint
{
pj_pool_t *pool; /** Pool to allocate memory for the endpoint. */
pj_mutex_t *mutex; /** Mutex for the pool, hash table, and event list/queue. */
pj_pool_factory *pf; /** Pool factory. */
pj_str_t name; /** Name. */
pj_timer_heap_t *timer_heap; /** Timer heap. */
pjsip_tpmgr *transport_mgr; /** Transport manager. */
pj_ioqueue_t *ioqueue; /** Ioqueue. */
pj_status_t ioq_last_err; /** Last ioqueue err */
pjsip_resolver_t *resolver; /** DNS Resolver. */
pj_rwmutex_t *mod_mutex; /** Modules lock. */
pjsip_module *modules[PJSIP_MAX_MODULE]; /** Modules. */
pjsip_module module_list; /** Module list, sorted by priority. */
pjsip_hdr cap_hdr; /** Capability header list. */
pjsip_hdr req_hdr; /** Additional request headers. */
exit_cb exit_cb_list; /** List of exit callback. */
};
先粗略看一下,结构成员, 从字面上可以猜到的成员函数相关的功能如下:
1) ioqueue 有了MEDIA Endpoint的经验, 这个应该是与SIP收发网络套接子轮询有关
2) timer_heap 这个应该与SIP协议实现的各种定时器有关
3) transport_mgr 这个应该与传输层的管理有关,是我们在pjsip_endpoint后将要研究的目标
4) mod_mutex
modules[] 这个三个应该与SIP消息的处理有关
module_list
5) req_hdr 这个可能与SIP请求消息的头部相关
6) req_hdr 这个可能与SIP请求消息的头部的头域相关
二) pjsip_endpoint的创建函数,有了MEDIA Endpoint的经验, 这个函数应该是做了一系列的初始化动作,特别是启动了网络层和SIP信令处理栈
现在我们还不清楚SIP信令处理栈的工作机制,只是从一)中的分析来看, 应该与mod_xxx成员由关系
为了方便,下面摘录的初始化函数中,我把代码中一些判断代码和日志相关代码省略了。
PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, const char *name, pjsip_endpoint **p_endpt)
{
。。。
endpt = PJ_POOL_ZALLOC_T(pool, pjsip_endpoint); /* Create endpoint. */
。。。
pj_list_init(&endpt->module_list); /* Init modules list. */
pj_list_init(&endpt->exit_cb_list); /* Initialize exit callback list. */
。。。
pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex); /* Create R/W mutex for module manipulation. */
。。。
init_sip_parser(); /* Init parser. */
pjsip_tel_uri_subsys_init(); /* Init tel: uri */
。。。
pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex ); /* Create mutex for the events, etc. */
。。。
/* Create timer heap to manage all timers within this endpoint. */
pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, &endpt->timer_heap);
/* Set recursive lock for the timer heap. */
pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);。
pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
/* Set maximum timed out entries to process in a single poll. */
pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap, PJSIP_MAX_TIMED_OUT_ENTRIES);
。。。
pj_ioqueue_create(endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); /* Create ioqueue. */
。。。
/* Create transport manager. */
pjsip_tpmgr_create( endpt->pool, endpt, &endpt_on_rx_msg, &endpt_on_tx_msg, &endpt->transport_mgr);
。。。
pjsip_resolver_create(endpt->pool, &endpt->resolver); /* Create asynchronous DNS resolver. */
。。。
pj_list_init(&endpt->req_hdr); /* Initialize request headers. */
mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool, PJSIP_MAX_FORWARDS_VALUE); /* Add "Max-Forwards" for request header. */
pj_list_insert_before( &endpt->req_hdr, mf_hdr);
。。。
pj_list_init(&endpt->cap_hdr);/* Initialize capability header list. */
/* Done. */
*p_endpt = endpt;
return status;
}
重点关注一下与传输层有关的两句:
1) pj_ioqueue_create(endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); /* Create ioqueue. */
2) pjsip_tpmgr_create( endpt->pool, endpt, &endpt_on_rx_msg, &endpt_on_tx_msg, &endpt->transport_mgr);
问题: endpt->ioqueue是在哪里及实现轮询让SIP信息流动起来的呢?
在pjsua_core.c中,可以看到使用工作线程进行SIP消息端口轮询的例子 :
pjsua_init()会启动如下的工作线程, pjsua_handle_events调用pjsip_endpt_handle_events2()实现对endpt->ioqueue的轮询
static int worker_thread(void *arg)
{
enum { TIMEOUT = 10 };
PJ_UNUSED_ARG(arg);
while (!pjsua_var.thread_quit_flag) {
int count;
count = pjsua_handle_events(TIMEOUT);
if (count < 0)
pj_thread_sleep(TIMEOUT);
}
return 0;
}
另外,在simpleua.c的main函数中, 也看到使用主任务实现SIP消息端口轮询的例子:
for (;!g_complete;) {
pj_time_val timeout = {0, 10};
pjsip_endpt_handle_events(g_endpt, &timeout);
}
至此, SIP被驱动起来了!明天研究下传输层及网络及其抽象层pjsip_transport:
struct udp_transport
{
pjsip_transport base;
pj_sock_t sock;
pj_ioqueue_key_t *key;
int rdata_cnt;
pjsip_rx_data **rdata;
int is_closing;
pj_bool_t is_paused;
int read_loop_spin;
/* Group lock to be used by UDP transport and ioqueue key */
pj_grp_lock_t *grp_lock;
};
struct pjsip_transport
{
char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
pj_pool_t *pool; /**< Pool used by transport. */
pj_atomic_t *ref_cnt; /**< Reference counter. */
pj_lock_t *lock; /**< Lock object. */
pj_bool_t tracing; /**< Tracing enabled? */
pj_bool_t is_shutdown; /**< Being shutdown? */
pj_bool_t is_destroying; /**< Destroy in progress? */
/** Key for indexing this transport in hash table. */
pjsip_transport_key key;
char *type_name; /**< Type name. */
unsigned flag; /**< #pjsip_transport_flags_e */
char *info; /**< Transport info/description.*/
int addr_len; /**< Length of addresses. */
pj_sockaddr local_addr; /**< Bound address. */
pjsip_host_port local_name; /**< Published name (eg. STUN). */
pjsip_host_port remote_name; /**< Remote address name. */
pjsip_transport_dir dir; /**< Connection direction. */
pjsip_endpoint *endpt; /**< Endpoint instance. */
pjsip_tpmgr *tpmgr; /**< Transport manager. */
pjsip_tpfactory *factory; /**< Factory instance. Note: it
may be invalid/shutdown. */
pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/
pj_timestamp last_recv_ts; /**< Last time receiving data. */
pj_size_t last_recv_len; /**< Last received data length. */
void *data; /**< Internal transport data. */
/**
* Function to be called by transport manager to send SIP message.
*
* @param transport The transport to send the message.
* @param packet The buffer to send.
* @param length The length of the buffer to send.
* @param op_key Completion token, which will be supplied to
* caller when pending send operation completes.
* @param rem_addr The remote destination address.
* @param addr_len Size of remote address.
* @param callback If supplied, the callback will be called
* once a pending transmission has completed. If
* the function completes immediately (i.e. return
* code is not PJ_EPENDING), the callback will not
* be called.
*
* @return Should return PJ_SUCCESS only if data has been
* succesfully queued to operating system for
* transmission. Otherwise it may return PJ_EPENDING
* if the underlying transport can not send the
* data immediately and will send it later, which in
* this case caller doesn't have to do anything
* except wait the calback to be called, if it
* supplies one.
* Other return values indicate the error code.
*/
pj_status_t (*send_msg)(pjsip_transport *transport,
pjsip_tx_data *tdata,
const pj_sockaddr_t *rem_addr,
int addr_len,
void *token,
pjsip_transport_callback callback);
/**
* Instruct the transport to initiate graceful shutdown procedure.
* After all objects release their reference to this transport,
* the transport will be deleted.
*
* Note that application MUST use #pjsip_transport_shutdown() instead.
*
* @param transport The transport.
*
* @return PJ_SUCCESS on success.
*/
pj_status_t (*do_shutdown)(pjsip_transport *transport);
/**
* Forcefully destroy this transport regardless whether there are
* objects that currently use this transport. This function should only
* be called by transport manager or other internal objects (such as the
* transport itself) who know what they're doing. Application should use
* #pjsip_transport_shutdown() instead.
*
* @param transport The transport.
*
* @return PJ_SUCCESS on success.
*/
pj_status_t (*destroy)(pjsip_transport *transport);
/*
* Application may extend this structure..
*/
};