pjsip学习笔记8

前面研究了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..
             */
        };

猜你喜欢

转载自blog.csdn.net/twd_1991/article/details/80609422