pjsua_lib示例之简单UA

本文介绍了如何利用pjsip2.5.5库中的samples工程,特别是`simpleua.c`文件,来创建一个无需注册到SIP服务器的基本UA。该UA支持基本呼叫、UDP在5060端口传输、RTP在4000端口传输、仅使用PCMA和PCMU编码器的语音媒体。通过调用`simpleua sip地址`命令,程序会进行SDP协商并启动RTP媒体传输。呼入呼叫会自动应答并挂断。代码示例展示了如何处理SIP请求和响应,以及媒体流的创建和管理。

这是pjsip2.5.5的samples工程内提供包括媒体及完整UA功能的简单应用,文件位置:pjproject-2.5.5\pjsip-apps\src\samples\simpleua.c,用户代理(UA)在SDK协商成功后启动RTP媒体传输。

此程序不需要注册到SIP服务器,它能够完成:基本呼叫、在5060端口传输UDP、在4000端口传输RTP、SDP协商、语音编解码器只支持PCMA和PCMU、声卡的语音媒体。

Samples工程编译完成后,在命令行输入:

       simpleua 要呼叫的sip地址

要呼叫的sip地址格式: sip:用户名@对端IP

 

*呼入的呼叫先以180自动应答,再以200自动应答,此程序单次呼叫后即退出。

 

 

#include <pjsip.h>

#include <pjmedia.h>

#include <pjmedia-codec.h>

#include <pjsip_ua.h>

#include <pjsip_simple.h>

#include <pjlib-util.h>

#include <pjlib.h>

 

#define THIS_FILE  "simpleua.c"

 

#include "util.h"

 

 

//设置

#define AF      pj_AF_INET()// 如果使用IPV6,将此行改为pj_AF_INET6()

                             //必须设置PJ_HAS_IPV6

                             //并且操作系统也需要支持 IPv6.  */

#if 0

#define SIP_PORT    5080             //侦听的SIP端口

#define RTP_PORT    5000             // RTP端口

#else

#define SIP_PORT    5060            //侦听的SIP端口

#define RTP_PORT    4000            // RTP端口

#endif

 

#define MAX_MEDIA_CNT   2       //媒体数量,设置为1将支持音频,

                              //设置为2,即支持音频,又支持视频

 

//全局静态变量

 

static pj_bool_t        g_complete;    //退出状态

static pjsip_endpoint      *g_endpt;        // SIP终端

static pj_caching_pool       cp;          //全局pool factory

 

static pjmedia_endpt       *g_med_endpt;   // 媒体终端

 

staticpjmedia_transport_info g_med_tpinfo[MAX_MEDIA_CNT];

                        //媒体传输用套接字信息

staticpjmedia_transport    *g_med_transport[MAX_MEDIA_CNT];

                        //媒体流传输端口

staticpjmedia_sock_info     g_sock_info[MAX_MEDIA_CNT]; 

                        //套接字信息组

 

//呼叫部分变量

staticpjsip_inv_session    *g_inv;      //当前invite传话

static pjmedia_stream       *g_med_stream;  //语音流

static pjmedia_snd_port     *g_snd_port;   //声卡设备

 

#if defined(PJMEDIA_HAS_VIDEO)&& (PJMEDIA_HAS_VIDEO != 0)

staticpjmedia_vid_stream   *g_med_vstream; //视频流

static pjmedia_vid_port    *g_vid_capturer;//视频捕获设备

static pjmedia_vid_port    *g_vid_renderer;//视频播放设备

#endif  //PJMEDIA_HAS_VIDEO

 

//函数声明

 

 

//呼叫过程上,当SDP协商完成后,回调此函数

static void  call_on_media_update( pjsip_inv_session *inv,

                 pj_status_t status);

 

//invite会话状态变化后,回调此函数

static void  call_on_state_changed( pjsip_inv_session *inv,

                   pjsip_event *e);

 

//当新对话建立后,回调此函数

static void  call_on_forked(pjsip_inv_session *inv, pjsip_event *e);

 

//对话之外,当收到请求时,回调此函数

static pj_bool_t  on_rx_request( pjsip_rx_data *rdata );

 

 

 

 

//PJSIP模块注册到应用用于处理对话或事务之外的请求,主要目的是处理INVITE请求消息,在那里将为它创建新的对话和INVITE会话

 

static pjsip_module  mod_simpleua =

{

    NULL, NULL,             /*prev, next.       */

    { "mod-simpleua", 12 },      /* Name.         */

    -1,                  /* Id            */

    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority          */

    NULL,                /* load()            */

    NULL,                /* start()           */

    NULL,                /* stop()            */

    NULL,                /* unload()          */

    &on_rx_request,          /* on_rx_request()       */

    NULL,                /* on_rx_response()      */

    NULL,                /* on_tx_request.        */

    NULL,                /* on_tx_response()      */

    NULL,                /* on_tsx_state()        */

};

 

 

//呼入消息通知

static pj_bool_t  logging_on_rx_msg(pjsip_rx_data *rdata)

{

    PJ_LOG(4,(THIS_FILE, "RX %d bytes%s from %s %s:%d:\n"

             "%.*s\n"

             "--end msg--",

             rdata->msg_info.len,

             pjsip_rx_data_get_info(rdata),

             rdata->tp_info.transport->type_name,

             rdata->pkt_info.src_name,

             rdata->pkt_info.src_port,

             (int)rdata->msg_info.len,

             rdata->msg_info.msg_buf));

   

    //此处必须返回false,否则其它消息将不被处理

    return PJ_FALSE;

}

 

//呼出消息通知

static pj_status_t  logging_on_tx_msg(pjsip_tx_data *tdata)

{

    

    /* Important note:

     *   tp_infofield is only valid after outgoing messages has passed

     *   transportlayer. So don't try to access tp_info when the module

     *   haslower priority than transport layer.

     */

 

    PJ_LOG(4,(THIS_FILE, "TX %d bytes%s to %s %s:%d:\n"

             "%.*s\n"

             "--end msg--",

             (tdata->buf.cur - tdata->buf.start),

             pjsip_tx_data_get_info(tdata),

             tdata->tp_info.transport->type_name,

             tdata->tp_info.dst_name,

             tdata->tp_info.dst_port,

             (int)(tdata->buf.cur - tdata->buf.start),

             tdata->buf.start));

 

    /* Always return success, otherwise message will notget sent! */

    return PJ_SUCCESS;

}

 

//模块实例

static pjsip_module  msg_logger =

{

    NULL, NULL,              /* prev, next.      */

    { "mod-msg-log", 13 },       /* Name.        */

    -1,                  /* Id           */

    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority        */

    NULL,                /* load()       */

    NULL,                /* start()      */

    NULL,                /* stop()       */

    NULL,                /* unload()     */

    &logging_on_rx_msg,          /* on_rx_request()  */

   &logging_on_rx_msg,          /* on_rx_response() */

    &logging_on_tx_msg,          /* on_tx_request.   */

    &logging_on_tx_msg,          /* on_tx_response() */

    NULL,                /* on_tsx_state()   */

 

};

 

 

/*

 * main()

 *

 * If called with argument, treat argument asSIP URL to be called.

 * Otherwise wait for incoming calls.

 */

int main(int argc, char *argv[])

{

    pj_pool_t  *pool = NULL;

    pj_status_t  status;

    unsigned i;

 

    /* 必须先初始化PJLIB*/

    status = pj_init();

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

    pj_log_set_level(5);

 

    /* 再初始化PJLIB-UTIL*/

    status = pjlib_util_init();

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

    /* 在分配内存之前必须创建pool factory*/

    pj_caching_pool_init(&cp,&pj_pool_factory_default_policy, 0);

 

 

    //创建全局终端

    {

    const  pj_str_t  *hostname;

    const  char  *endpt_name;

 

    //终端必须分配全局唯一名称,此处简单地使用主机名实现

    hostname = pj_gethostname();

    endpt_name = hostname->ptr;

 

    //创建终端

    status = pjsip_endpt_create(&cp.factory,endpt_name,

                    &g_endpt);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

    }

 

 

//添加UDP传输端口

//如果已经存在了UDP套接字时,应用可使用pjsip_udp_transport_attach()函数启动UDP传输端口

    {

    pj_sockaddr  addr;

 

    pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT);

   

    if (AF == pj_AF_INET()) {

       status = pjsip_udp_transport_start( g_endpt, &addr.ipv4, NULL,

                        1, NULL);

    } else if (AF == pj_AF_INET6()) {

       status = pjsip_udp_transport_start6(g_endpt, &addr.ipv6, NULL,

                        1, NULL);

    } else {

       status = PJ_EAFNOTSUP;

    }

 

    if (status != PJ_SUCCESS) {

       app_perror(THIS_FILE, "Unableto start UDP transport", status);

        return 1;

    }

    }

 

 

    //初始化事务层,将创建/初始化传输hash

    status =pjsip_tsx_layer_init_module(g_endpt);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

//初始化UA层模块,将创建/初始化对话hash

    status = pjsip_ua_init_module( g_endpt, NULL );

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

//初始化invite会话模块,将初始化会话附加参数,例如与事件相关的回调函数

//on_state_changedon_new_session是应用必须支持的回调函数

//我们可以让应用程序在on_media_update()回调函数中,启动媒体传输

    {

    pjsip_inv_callback  inv_cb;

 

    // 初始化INVITE会话回调

    pj_bzero(&inv_cb, sizeof(inv_cb));

    inv_cb.on_state_changed =&call_on_state_changed;

    inv_cb.on_new_session = &call_on_forked;

    inv_cb.on_media_update =&call_on_media_update;

 

    //初始化INVITE会话模块

    status = pjsip_inv_usage_init(g_endpt,&inv_cb);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

    }

 

    //初始化100rel支持

    status = pjsip_100rel_init_module(g_endpt);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS,status);

 

    //注册用于接收呼入请求的自己模块

    status = pjsip_endpt_register_module(g_endpt, &mod_simpleua);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

//注册消息日志模块

    status = pjsip_endpt_register_module(g_endpt, &msg_logger);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

//初始化媒体终端,它将隐性地启动PJMEDIA的相关功能

#if PJ_HAS_THREADS

    status =pjmedia_endpt_create(&cp.factory, NULL, 1, &g_med_endpt);

#else

    status =pjmedia_endpt_create(&cp.factory,

                 pjsip_endpt_get_ioqueue(g_endpt),

                 0, &g_med_endpt);

#endif

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

//给媒体终端增加PCMA/PCMU编解码器

#if defined(PJMEDIA_HAS_G711_CODEC)&& PJMEDIA_HAS_G711_CODEC!=0

    status = pjmedia_codec_g711_init(g_med_endpt);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

#endif

 

 

#if defined(PJMEDIA_HAS_VIDEO)&& (PJMEDIA_HAS_VIDEO != 0)

    //初始化视频子系统

    pool =pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512);

    status = pjmedia_video_format_mgr_create(pool,64, 0, NULL);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

    status = pjmedia_converter_mgr_create(pool,NULL);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

    status = pjmedia_vid_codec_mgr_create(pool,NULL);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

    status =pjmedia_vid_dev_subsys_init(&cp.factory);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

#   if defined(PJMEDIA_HAS_OPENH264_CODEC) &&PJMEDIA_HAS_OPENH264_CODEC != 0

    status =pjmedia_codec_openh264_vid_init(NULL, &cp.factory);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

#   endif

 

#  if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) &&PJMEDIA_HAS_FFMPEG_VID_CODEC!=0

    //初始化ffmpeg视频编解码器

    status =pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

#  endif  /* PJMEDIA_HAS_FFMPEG_VID_CODEC */

 

#endif  /* PJMEDIA_HAS_VIDEO */

   

//创建用于RTP/RTCP套接字发送/接收的媒体传输端口

//每个呼叫均需要一个媒体传输端口,应用程序可以选择地复用相同的媒体传输端口用于后续呼叫

    for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) {

    status = pjmedia_transport_udp_create3(g_med_endpt,AF,NULL, NULL,

                           RTP_PORT + i*2, 0,

                           &g_med_transport[i]);

    if (status != PJ_SUCCESS) {

       app_perror(THIS_FILE, "Unableto create media transport", status);

        return 1;

    }

 

    //取媒体传输端口的套接字信息(地址,端口),我们需要利用它们创建SDP

    pjmedia_transport_info_init(&g_med_tpinfo[i]);

    pjmedia_transport_get_info(g_med_transport[i],&g_med_tpinfo[i]);

 

    pj_memcpy(&g_sock_info[i],&g_med_tpinfo[i].sock_info,

          sizeof(pjmedia_sock_info));

    }

 

//如果提供呼叫的URL,则创建立即创建呼叫

    if (argc > 1) {

    pj_sockaddr hostaddr;

    char hostip[PJ_INET6_ADDRSTRLEN+2];

    char temp[80];

    pj_str_t dst_uri = pj_str(argv[1]);

    pj_str_t local_uri;

    pjsip_dialog *dlg;

    pjmedia_sdp_session *local_sdp;

    pjsip_tx_data *tdata;

 

    if (pj_gethostip(AF, &hostaddr) !=PJ_SUCCESS) {

       app_perror(THIS_FILE, "Unableto retrieve local host IP", status);

        return 1;

    }

    pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);

 

    pj_ansi_sprintf(temp, "<sip:simpleuac@%s:%d>",

            hostip, SIP_PORT);

    local_uri = pj_str(temp);

 

    //创建UAC对话

    status = pjsip_dlg_create_uac(pjsip_ua_instance(),

                       &local_uri,  /* local URI */

                       &local_uri,  /* local Contact */

                       &dst_uri,    /* remote URI */

                       &dst_uri,    /* remote target */

                      &dlg);        /* dialog */

    if (status != PJ_SUCCESS) {

       app_perror(THIS_FILE, "Unableto create UAC dialog", status);

        return 1;

    }

 

    //如果怕呼出的INVITE被对方怀疑,我们可以在对话中加入凭证,如下例:

    /*

        {

        pjsip_cred_info cred[1];

 

        cred[0].realm     = pj_str("sip.server.realm");

        cred[0].scheme    = pj_str("digest");

        cred[0].username  = pj_str("theuser");

        cred[0].data_type =PJSIP_CRED_DATA_PLAIN_PASSWD;

        cred[0].data      = pj_str("thepassword");

 

        pjsip_auth_clt_set_credentials(&dlg->auth_sess, 1, cred);

        }

     */

 

 

    /* Get the SDP body to be put in the outgoingINVITE, by asking

     *media endpoint to create one for us.

     */

    status = pjmedia_endpt_create_sdp(g_med_endpt,     /* the media endpt   */

                       dlg->pool,       /* pool.     */

                       MAX_MEDIA_CNT,  /* # of streams  */

                       g_sock_info,     /* RTP sock info */

                       &local_sdp);     /* the SDP result    */

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

 

    //创建INVITE传话,方便地将SDP作为初始参数传递给会话

    status = pjsip_inv_create_uac( dlg,local_sdp, 0, &g_inv);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

    /* If we want the initial INVITE to travel tospecific SIP proxies,

     *then we should put the initial dialog's route set here. The final

     *route set will be updated once a dialog has been established.

     * Toset the dialog's initial route set, we do it with something

     *like this:

     *

        {

        pjsip_route_hdr route_set;

        pjsip_route_hdr *route;

        const pj_str_t hname = {"Route", 5 };

        char *uri ="sip:proxy.server;lr";

 

        pj_list_init(&route_set);

 

        route = pjsip_parse_hdr( dlg->pool,&hname,

                     uri, strlen(uri),

                     NULL);

        PJ_ASSERT_RETURN(route != NULL, 1);

        pj_list_push_back(&route_set,route);

 

        pjsip_dlg_set_route_set(dlg,&route_set);

        }

     *

     *Note that Route URI SHOULD have an ";lr" parameter!

     */

 

    //创建INVITE初始请求

//INVITE请求须包含完整的请求和SDP内容

    status = pjsip_inv_invite(g_inv,&tdata);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

 

    //发送INVITE请求

//INVITE传话状态会通过回调传回

    status = pjsip_inv_send_msg(g_inv, tdata);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

 

 

    } else {

 

    //没有外呼的URL

 

    PJ_LOG(3,(THIS_FILE, "Ready toaccept incoming calls..."));

    }

 

 

    //循环,直到某个呼叫完成

    for (;!g_complete;) {

    pj_time_val timeout = {0, 10};

    pjsip_endpt_handle_events(g_endpt,&timeout);

    }

 

    //退出时,转储当前内存使用情况

    dump_pool_usage(THIS_FILE, &cp);

 

//销毁音频端口

//因为音频端口有线程存/取帧数据,故需要在流之前销毁音频端口

    if (g_snd_port)

    pjmedia_snd_port_destroy(g_snd_port);

 

#if defined(PJMEDIA_HAS_VIDEO)&& (PJMEDIA_HAS_VIDEO != 0)

    //销毁视频端口

    if (g_vid_capturer)

    pjmedia_vid_port_destroy(g_vid_capturer);

    if (g_vid_renderer)

    pjmedia_vid_port_destroy(g_vid_renderer);

#endif

 

    //销毁流

    if (g_med_stream)

    pjmedia_stream_destroy(g_med_stream);

#if defined(PJMEDIA_HAS_VIDEO)&& (PJMEDIA_HAS_VIDEO != 0)

    if (g_med_vstream)

    pjmedia_vid_stream_destroy(g_med_vstream);

 

    //ffmpeg

#   if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) &&PJMEDIA_HAS_FFMPEG_VID_CODEC!=0

    pjmedia_codec_ffmpeg_vid_deinit();

#   endif

#   if defined(PJMEDIA_HAS_OPENH264_CODEC) &&PJMEDIA_HAS_OPENH264_CODEC != 0

    pjmedia_codec_openh264_vid_deinit();

#   endif

 

#endif

 

    //销毁媒体传输端口

    for (i = 0; i < MAX_MEDIA_CNT; ++i) {

    if (g_med_transport[i])

       pjmedia_transport_close(g_med_transport[i]);

    }

 

    //完结媒体终端

    if (g_med_endpt)

    pjmedia_endpt_destroy(g_med_endpt);

 

    //完结sip终端

    if (g_endpt)

    pjsip_endpt_destroy(g_endpt);

 

    //释放pool

    if (pool)

    pj_pool_release(pool);

 

    return 0;

}

 

 

 

//INVITE会话状态改变时的回调函数。

//此函数是在INVITE传话模块初始时注册,我们通常在此得到INVITE传话中断,并退出应用。

static void  call_on_state_changed( pjsip_inv_session *inv,

                   pjsip_event *e)

{

    PJ_UNUSED_ARG(e);

 

    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {

 

    PJ_LOG(3,(THIS_FILE, "CallDISCONNECTED [reason=%d (%s)]",

         inv->cause,

         pjsip_get_status_text(inv->cause)->ptr));

 

    PJ_LOG(3,(THIS_FILE, "One callcompleted, application quitting..."));

    g_complete = 1;

 

    } else {

 

    PJ_LOG(3,(THIS_FILE, "Call statechanged to %s",

         pjsip_inv_state_name(inv->state)));

 

    }

}

 

 

//当会话复制完成后,此函数被回调

static void  call_on_forked(pjsip_inv_session *inv, pjsip_event *e)

{

    /* To be done... */

    PJ_UNUSED_ARG(inv);

    PJ_UNUSED_ARG(e);

}

 

 

//当外部呼叫的任意对话或事务到达时,此函数被回调。我们可以控制想接的请求,其他的回应500拒绝

static pj_bool_t  on_rx_request( pjsip_rx_data *rdata )

{

    pj_sockaddr hostaddr;

    char temp[80], hostip[PJ_INET6_ADDRSTRLEN];

    pj_str_t local_uri;

    pjsip_dialog *dlg;

    pjmedia_sdp_session *local_sdp;

    pjsip_tx_data *tdata;

    unsigned options = 0;

    pj_status_t status;

 

 

//500回应(无状态)任何非INVITE请求

    if (rdata->msg_info.msg->line.req.method.id !=PJSIP_INVITE_METHOD) {

 

    if (rdata->msg_info.msg->line.req.method.id !=PJSIP_ACK_METHOD) {

       pj_str_t reason = pj_str("Simple UA unable to handle "

                     "this request");

 

       pjsip_endpt_respond_stateless( g_endpt, rdata,

                       500, &reason,

                       NULL, NULL);

    }

    return PJ_TRUE;

    }

 

 

//如果此INVITE会话已经处理,则拒绝

    if (g_inv) {

 

    pj_str_t reason = pj_str("Another callis in progress");

 

    pjsip_endpt_respond_stateless( g_endpt,rdata,

                       500, &reason,

                       NULL, NULL);

    return PJ_TRUE;

 

    }

 

    //效验下我们要控制的请求

    status = pjsip_inv_verify_request(rdata,&options, NULL, NULL,

                      g_endpt, NULL);

    if (status != PJ_SUCCESS) {

 

    pj_str_t reason = pj_str("Sorry SimpleUA can not handle this INVITE");

 

    pjsip_endpt_respond_stateless( g_endpt,rdata,

                       500, &reason,

                       NULL, NULL);

    return PJ_TRUE;

    }

 

    //生成联系URI

    if (pj_gethostip(AF, &hostaddr) !=PJ_SUCCESS) {

    app_perror(THIS_FILE, "Unable toretrieve local host IP", status);

    return PJ_TRUE;

    }

    pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);

 

    pj_ansi_sprintf(temp, "<sip:simpleuas@%s:%d>",

           hostip, SIP_PORT);

    local_uri = pj_str(temp);

 

    //创建UAS对话

    status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(),

                        rdata,

                        &local_uri, /* contact */

                        &dlg);

    if (status != PJ_SUCCESS) {

    pjsip_endpt_respond_stateless(g_endpt,rdata, 500, NULL,

                      NULL, NULL);

    return PJ_TRUE;

    }

 

//从媒体终端得到媒体能力集

    status = pjmedia_endpt_create_sdp( g_med_endpt,rdata->tp_info.pool,

                       MAX_MEDIA_CNT, g_sock_info,&local_sdp);

    pj_assert(status == PJ_SUCCESS);

    if (status != PJ_SUCCESS) {

    pjsip_dlg_dec_lock(dlg);

    return PJ_TRUE;

    }

 

 

//传递UAS会话和SDP能力集,创建INVITE会话

    status = pjsip_inv_create_uas( dlg, rdata,local_sdp, 0, &g_inv);

    pj_assert(status == PJ_SUCCESS);

    if (status != PJ_SUCCESS) {

    pjsip_dlg_dec_lock(dlg);

    return PJ_TRUE;

    }

 

//INVITE传话已经创建,释放对话锁定

    pjsip_dlg_dec_lock(dlg);

 

 

//首先发送180回应

//最先发送的回应必须用pjsip_inv_initial_answer()创建,后面相同事务的回应必须用pjsip_inv_answer()

    status = pjsip_inv_initial_answer(g_inv,rdata,

                      180,

                      NULL, NULL, &tdata);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS,PJ_TRUE);

 

 

    //发送180回应 

    status = pjsip_inv_send_msg(g_inv, tdata);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS,PJ_TRUE);

 

 

    //现在创建200回应

    status = pjsip_inv_answer( g_inv,

                   200, NULL,    /* st_code and st_text */

                   NULL,     /* SDP already specified */

                   &tdata);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS,PJ_TRUE);

 

    //发送200回应

    status = pjsip_inv_send_msg(g_inv, tdata);

    PJ_ASSERT_RETURN(status == PJ_SUCCESS,PJ_TRUE);

 

 

//完成。当呼叫中断时,将在回调中收到报告

 

    return PJ_TRUE;

}

 

 

 

//SDP协商完成时,收到回调。我们要到此回调内快速启动媒体

static void  call_on_media_update( pjsip_inv_session *inv,

                 pj_status_t status)

{

    pjmedia_stream_info stream_info;

    const pjmedia_sdp_session *local_sdp;

    const pjmedia_sdp_session *remote_sdp;

    pjmedia_port *media_port;

 

    if (status != PJ_SUCCESS) {

 

    app_perror(THIS_FILE, "SDPnegotiation has failed", status);

 

    /* Here we should disconnect call if we're not inthe middle

     * ofinitializing an UAS dialog and if this is not a re-INVITE.

     */

    return;

    }

 

    //取本地和远程SDP。我们需要这些SDP创建媒体会话

    status =pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);

 

    status =pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);

 

 

    //SDP中的音频媒体描述创建流信息

    status =pjmedia_stream_info_from_sdp(&stream_info, inv->dlg->pool,

                      g_med_endpt,

                      local_sdp, remote_sdp, 0);

    if (status != PJ_SUCCESS) {

    app_perror(THIS_FILE,"Unable tocreate audio stream info",status);

    return;

    }

 

    //如果需要,在创建流之前,我们可以更改流信息中的设置,如jitter设置、编解码器设置等等。

 

//通过流信息和媒体套接字,轻松创建新的音频媒体流

    status = pjmedia_stream_create(g_med_endpt,inv->dlg->pool, &stream_info,

                   g_med_transport[0], NULL, &g_med_stream);

    if (status != PJ_SUCCESS) {

    app_perror( THIS_FILE, "Unable tocreate audio stream", status);

    return;

    }

 

    //启动音频流

    status =pjmedia_stream_start(g_med_stream);

    if (status != PJ_SUCCESS) {

    app_perror( THIS_FILE, "Unable tostart audio stream", status);

    return;

    }

 

//取音频流的媒体端口接口。媒体端口接口基本结构包括get_frame()put_frame()函数,

//我们可以将此媒体端口接口连接到会议桥,或直接到一个用于录音/放音的声卡设备

    pjmedia_stream_get_port(g_med_stream,&media_port);

 

    //创建声卡端口

    pjmedia_snd_port_create(inv->pool,

                            PJMEDIA_AUD_DEFAULT_CAPTURE_DEV,

                           PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV,

                           PJMEDIA_PIA_SRATE(&media_port->info),//时间速率

                           PJMEDIA_PIA_CCNT(&media_port->info),//通话数

                           PJMEDIA_PIA_SPF(&media_port->info), //每帧采样

                           PJMEDIA_PIA_BITS(&media_port->info),//每次采样bit

                            0,

                            &g_snd_port);

 

    if (status != PJ_SUCCESS) {

    app_perror( THIS_FILE, "Unable tocreate sound port", status);

    PJ_LOG(3,(THIS_FILE, "%d %d %d%d",

           PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate       */

           PJMEDIA_PIA_CCNT(&media_port->info),/* channel count    */

           PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/

           PJMEDIA_PIA_BITS(&media_port->info) /* bits per sample  */

       ));

    return;

    }

 

    status =pjmedia_snd_port_connect(g_snd_port, media_port);

 

 

//取会话中第2个流的媒体端口接口,视频流的话,我们可以将此媒体端口接口直接连接到渲染/捕获视频设备

#if defined(PJMEDIA_HAS_VIDEO)&& (PJMEDIA_HAS_VIDEO != 0)

    if (local_sdp->media_count > 1) {

    pjmedia_vid_stream_info vstream_info;

    pjmedia_vid_port_param vport_param;

 

    pjmedia_vid_port_param_default(&vport_param);

 

    //通过SDP中的视频媒体描述合建视频信息

    status = pjmedia_vid_stream_info_from_sdp(&vstream_info,

                          inv->dlg->pool, g_med_endpt,

                          local_sdp, remote_sdp, 1);

    if (status != PJ_SUCCESS) {

       app_perror(THIS_FILE,"Unable to create video stream info",status);

        return;

    }

 

    //如果需要,在创建视频流之前,我们可以更改流信息中的设置,如jitter设置、编解码器设置等等。

 

    //通过流信息和媒体套接字参数,创建新的视频媒体流

    status = pjmedia_vid_stream_create(g_med_endpt,NULL, &vstream_info,

                                      g_med_transport[1], NULL,

                                      &g_med_vstream);

    if (status != PJ_SUCCESS) {

       app_perror( THIS_FILE, "Unable to create video stream", status);

        return;

    }

 

    //启动视频流

    status =pjmedia_vid_stream_start(g_med_vstream);

    if (status != PJ_SUCCESS) {

       app_perror( THIS_FILE, "Unable to start video stream", status);

        return;

    }

 

    if (vstream_info.dir & PJMEDIA_DIR_DECODING) {

       status = pjmedia_vid_dev_default_param(

                inv->pool,PJMEDIA_VID_DEFAULT_RENDER_DEV,

                &vport_param.vidparam);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable to getdefault param of video "

              "rendererdevice",status);

        return;

        }

 

        //取解码的视频流

       pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_DECODING,

                    &media_port);

 

        //设置格式

       pjmedia_format_copy(&vport_param.vidparam.fmt,

                &media_port->info.fmt);

       vport_param.vidparam.dir = PJMEDIA_DIR_RENDER;

       vport_param.active = PJ_TRUE;

 

        //创建渲染端口

       status = pjmedia_vid_port_create(inv->pool, &vport_param,

                         &g_vid_renderer);

        if (status != PJ_SUCCESS){

        app_perror(THIS_FILE, "Unable tocreate video renderer device",

              status);

        return;

        }

 

        //连接渲染端口到媒体端口

       status = pjmedia_vid_port_connect(g_vid_renderer, media_port,

                          PJ_FALSE);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable toconnect renderer to stream",

              status);

        return;

        }

    }

 

    //创建捕获设备

    if (vstream_info.dir & PJMEDIA_DIR_ENCODING) {

       status = pjmedia_vid_dev_default_param(

                inv->pool, PJMEDIA_VID_DEFAULT_CAPTURE_DEV,

                &vport_param.vidparam);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable to getdefault param of video "

              "capturedevice",status);

        return;

        }

 

        //取编码的视频流端口

       pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_ENCODING,

                    &media_port);

 

        //从流信息中取捕获格式

       pjmedia_format_copy(&vport_param.vidparam.fmt,

                           &media_port->info.fmt);

       vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;

       vport_param.active = PJ_TRUE;

 

        //创建录音端口

       status = pjmedia_vid_port_create(inv->pool, &vport_param,

                         &g_vid_capturer);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable tocreate video capture device",

              status);

        return;

        }

 

        //连接录音媒体端口

       status = pjmedia_vid_port_connect(g_vid_capturer, media_port,

                          PJ_FALSE);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable toconnect capturer to stream",

              status);

        return;

        }

    }

 

    //启动流

    if (g_vid_renderer) {

       status = pjmedia_vid_port_start(g_vid_renderer);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable tostart video renderer",

              status);

        return;

        }

    }

    if (g_vid_capturer) {

       status = pjmedia_vid_port_start(g_vid_capturer);

        if (status !=PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable tostart video capturer",

              status);

        return;

        }

    }

    }

#endif  /* PJMEDIA_HAS_VIDEO */

 

    //媒体部分完成

}

pj_status_t pjsua_call_make_call( pjsua_acc_id acc_id, pj_str_t *dest_uri, int seq, unsigned options, void *user_data, const pjsua_msg_data *msg_data, const pjsua_call_other *other, pjsua_call_id *p_call_id) { pj_pool_t *tmp_pool = NULL; pjsip_dialog *dlg = NULL; pjmedia_sdp_session *offer = NULL; pjsip_inv_session *inv = NULL; pjsua_acc *acc = NULL; pjsua_call *call = NULL; int call_id = -1; pj_str_t contact = {NULL, 0}; pjsip_tx_data *tdata = NULL; pj_status_t status; # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 pj_str_t local_uri = {NULL, 0}; # endif # if PJ_FAKE_CALL_SUPPORT pj_bool_t bfakeCall = (0 == dest_uri->slen) ? PJ_TRUE : PJ_FALSE; # endif pj_bool_t bCreateFwRule = PJ_TRUE; # if PJ_FAKE_CALL_SUPPORT bCreateFwRule = !bfakeCall; # endif #ifdef INCLUDE_RFC3325 pjsip_generic_string_hdr privacyHdr; pjsip_p_prefered_identity_hdr *preferedIdHdr; pj_str_t preferedId_Url_Str = {NULL, 0}; pjsip_uri *preferedIdUri = NULL; pj_str_t hname = {"", 0}; pj_str_t hvalue = {"", 0}; #if PJ_RFC3960_SUPPORT char localIp[40]; pj_uint16_t rtpPort; pj_uint16_t rtcpPort; int family; #endif #endif /* INCLUDE_RFC3325 */ #if PJ_FAKE_CALL_SUPPORT if (bfakeCall) { dest_uri->slen = sprintf(dest_uri->ptr, "%s", "sip:192.168.1.100"); } #endif #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Ready to make a call!"); #if PJ_FAKE_CALL_SUPPORT } #endif /* Check that account is valid */ PJ_ASSERT_RETURN( # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 -1==acc_id || # endif ( acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc)), PJ_EINVAL); /* Check arguments */ PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "argument:accId %d, destUri(%.*s)", acc_id, dest_uri->slen, pj_strnull(dest_uri->ptr)); #if PJ_FAKE_CALL_SUPPORT } #endif PJSUA_LOCK(); /* Create sound port if none is instantiated, to check if sound device * can be used. But only do this with the conference bridge, as with * audio switchboard (i.e. APS-Direct), we can only open the sound * device once the correct format has been known */ /*ycw-pjsip-delete conference*/ #if 0 if (!pjsua_var.is_mswitch #if 0 /*ycw-pjsip-20110610-delete sound device*/ && pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL && !pjsua_var.no_snd #endif ) { /*ycw-pjsip-20110610-delete sound device*/ #if 0 pj_status_t status; status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev); if (status != PJ_SUCCESS) { PJSUA_UNLOCK(); return status; } #endif } #endif # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (acc_id >= 0) { # endif acc = &pjsua_var.acc[acc_id]; if (!acc->valid # if defined(SUPPORT_ACCOUNT_RTT) && SUPPORT_ACCOUNT_RTT!=0 || !acc->regDuration /*ycw-pjsip-regDuration*/ # elif !defined(INCLUDE_EMERGENCY_CALL) || !acc->regOK # endif ) { pjsua_perror(THIS_FILE, "Unable to make call because account " "is not valid or not active", PJ_EINVALIDOP); cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Unable to make call because account " "is not valid or not active"); PJSUA_UNLOCK(); return PJ_EINVALIDOP; } # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 } # endif /* Find free call slot. */ call_id = alloc_call_id(); if (call_id == PJSUA_INVALID_ID) { pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Alloc call id error"); #if PJ_FAKE_CALL_SUPPORT } #endif PJSUA_UNLOCK(); return PJ_ETOOMANY; } call = &pjsua_var.calls[call_id]; /* Associate session with account */ call->acc_id = acc_id; # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (acc) { # endif call->call_hold_type = acc->cfg.call_hold_type; # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 } else { call->call_hold_type = PJSUA_CALL_HOLD_TYPE_DEFAULT; } # endif call->seq = seq; /* Create temporary pool */ tmp_pool = pjsua_pool_create("tmpcall10", 512, 256); if (NULL == tmp_pool) { pjsua_perror(THIS_FILE, "Error create pool", PJ_ENOMEM); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Create pool error!"); #if PJ_FAKE_CALL_SUPPORT } #endif PJSUA_UNLOCK(); return PJ_ENOMEM; } /* Verify that destination URI is valid before calling * pjsua_acc_create_uac_contact, or otherwise there * a misleading "Invalid Contact URI" error will be printed * when pjsua_acc_create_uac_contact() fails. */ if (1) { pjsip_uri *uri; pj_str_t dup; pj_strdup_with_null(tmp_pool, &dup, dest_uri); //printf("going to parse the string: %.*s\n", (int)dup.slen, pj_strnull(dup.ptr)); uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0); if (uri == NULL) { pjsua_perror(THIS_FILE, "Unable to make call", PJSIP_EINVALIDREQURI); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Parse uri error!"); #if PJ_FAKE_CALL_SUPPORT } #endif pj_pool_release(tmp_pool); PJSUA_UNLOCK(); return PJSIP_EINVALIDREQURI; } } PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id, (int)dest_uri->slen, pj_strnull(dest_uri->ptr))); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Making call with acc #%d to %.*s", acc_id, (int)dest_uri->slen, pj_strnull(dest_uri->ptr)); #if PJ_FAKE_CALL_SUPPORT } #endif /* Mark call start time. */ pj_gettimeofday(&call->start_time); /* Reset first response time */ call->res_time.sec = 0; /* Create suitable Contact header unless a Contact header has been * set in the account. */ if ( # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 acc && # endif acc->contact.slen ) { contact = acc->contact; } else { status = pjsua_acc_create_uac_contact(tmp_pool, &contact, acc_id, dest_uri); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Unable to generate Contact header"); #if PJ_FAKE_CALL_SUPPORT } #endif pj_pool_release(tmp_pool); PJSUA_UNLOCK(); return status; } } /* Create outgoing dialog: */ #if 0 status = pjsip_dlg_create_uac( pjsip_ua_instance(), &acc->cfg.id, &contact, dest_uri, dest_uri, &dlg); #else #ifdef INCLUDE_RFC3325 if (acc && (PJ_TRUE == acc->cfg.supportNAI)) { local_uri.ptr = (char*)pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); local_uri.slen = pj_ansi_snprintf(local_uri.ptr, PJSIP_MAX_URL_SIZE, "\"Anonymous\"<sip:%s@%.*s>", acc->cfg.preferId.ptr, (int)acc->srv_domain.slen, pj_strnull(acc->srv_domain.ptr)); PJ_LOG(1,(THIS_FILE, "Making Anonymous call with acc #%d to %.*s", acc_id, (int)local_uri.slen, pj_strnull(local_uri.ptr))); status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, &contact, dest_uri, dest_uri, &dlg); options = 0; // reset } else #endif /* INCLUDE_RFC3325 */ if (options == pj_call_anonymous) { pj_str_t anonymous_uri = {"<sip:anonymous@anonymous.invalid>", 33}; status = pjsip_dlg_create_uac( pjsip_ua_instance(), &anonymous_uri, &contact, dest_uri, dest_uri, &dlg); options = 0; // reset } # if (defined(NUM_VOICEAPP_CHANNELS) && 0!=NUM_VOICEAPP_CHANNELS) else if (options == pj_call_voiceapp_anonymous) { pj_str_t anonymous_uri = {"<sip:anonymous@anonymous.invalid>", 33}; status = pjsip_dlg_create_uac( pjsip_ua_instance(), &anonymous_uri, &contact, dest_uri, dest_uri, &dlg); call->isVoiceapp = PJ_TRUE; options = 0; // reset } else if(options == pj_call_voiceapp) { # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (NULL == acc && pjsua_var.BoundIp.slen > 0) { local_uri.ptr = (char*)pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); local_uri.slen = pj_ansi_snprintf(local_uri.ptr, PJSIP_MAX_URL_SIZE, "<sip:%.*s>", (int)pjsua_var.BoundIp.slen, pjsua_var.BoundIp.ptr); status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, &contact, dest_uri, dest_uri, &dlg); } else # endif { pj_str_t *local_str = &acc->cfg.id; if (acc->cfg.lineId.slen > 0 ) { CMSIP_PRINT("=========the acc has lineid,replace it!!!============\n"); local_str = &acc->cfg.lineId; } status = pjsip_dlg_create_uac( pjsip_ua_instance(), local_str, &contact, dest_uri, dest_uri, &dlg); } /*call->isVoiceapp = PJ_TRUE; options = 0;*/ } #endif else { # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (NULL == acc && pjsua_var.BoundIp.slen > 0) { local_uri.ptr = (char*)pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); local_uri.slen = pj_ansi_snprintf(local_uri.ptr, PJSIP_MAX_URL_SIZE, "<sip:%.*s>", (int)pjsua_var.BoundIp.slen, pjsua_var.BoundIp.ptr); status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, &contact, dest_uri, dest_uri, &dlg); } else # endif { pj_str_t *local_str = &acc->cfg.id; if (acc->cfg.lineId.slen > 0 ) { CMSIP_PRINT("=========the acc has lineid,replace it!!!============\n"); local_str = &acc->cfg.lineId; } status = pjsip_dlg_create_uac( pjsip_ua_instance(), local_str, &contact, dest_uri, dest_uri, &dlg); } } #endif if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Dialog creation failed", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Dialog creation failed"); #if PJ_FAKE_CALL_SUPPORT } #endif pj_pool_release(tmp_pool); PJSUA_UNLOCK(); return status; } /* Increment the dialog's lock otherwise when invite session creation * fails the dialog will be destroyed prematurely. */ pjsip_dlg_inc_lock(dlg); /* Calculate call's secure level */ call->secure_level = get_secure_level(acc_id, dest_uri); /* Init media channel */ status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, call->secure_level, dlg->pool, NULL, NULL); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Error initializing media channel"); #if PJ_FAKE_CALL_SUPPORT } #endif goto on_error; } /* Create offer */ /*ycw-pjsip. t38*/ #if 0 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL, &offer, NULL); #else /*ycw-pjsip-codec*/ /*We must set the sdp's codec list according to the order of the codec list of the account.*/ if (options == pj_call_t38) { status = pjsua_media_channel_create_sdp_t38(call->index, dlg->pool, NULL, &offer, NULL, bCreateFwRule); options = 0; } else { status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL, &offer, NULL, bCreateFwRule); } if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating sdp", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Create sdp error!"); #if PJ_FAKE_CALL_SUPPORT } #endif goto on_error; } # if defined(INCLUDE_PSTN_GATEWAY) if (pj_call_pstn == options) { call->isPstn = PJ_TRUE; # if (defined(PJ_MEDIA_TRANSIT_BY_PJSIP) && 0==PJ_MEDIA_TRANSIT_BY_PJSIP) && \ (defined(INCLUDE_PSTN_GATEWAY) || defined(INCLUDE_USB_VOICEMAIL)) if (PJ_FALSE == call->medTpReady) { status = pjsua_media_transport_create_for_single_call(call_id); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "create media transport for single call error", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Create media transport error!"); #if PJ_FAKE_CALL_SUPPORT } #endif goto on_error; } call->medTpReady = PJ_TRUE; } # endif } else { call->isPstn = PJ_FALSE; } # endif #endif /*ycw-pjsip. t38*/ #if defined(PJMEDIA_HAS_SRTP) && PJMEDIA_HAS_SRTP!= 0 int audio_index; # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (NULL == acc) { audio_index = find_audio_index(offer, pjsua_var.ua_cfg.use_srtp); } else { # endif audio_index = find_audio_index(offer, pjsua_var.acc[call->acc_id].cfg.use_srtp); # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 } # endif #else int audio_index = find_audio_index(offer, PJ_FALSE); #endif if (audio_index < 0) audio_index = 0; call->request_channel_mode = pjsua_call_acquire_channel_mode(offer, audio_index); /* Create the INVITE session: */ options |= PJSIP_INV_SUPPORT_100REL; if (NULL == acc) { if (pjsua_var.ua_cfg.require_100rel) { options |= PJSIP_INV_REQUIRE_100REL; } if (pjsua_var.ua_cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) { options |= PJSIP_INV_SUPPORT_TIMER; if (pjsua_var.ua_cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED) { options |= PJSIP_INV_REQUIRE_TIMER; } else if (pjsua_var.ua_cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS) { options |= PJSIP_INV_ALWAYS_USE_TIMER; } } } else { if (acc->cfg.require_100rel) { options |= PJSIP_INV_REQUIRE_100REL; } if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) { options |= PJSIP_INV_SUPPORT_TIMER; if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED) { options |= PJSIP_INV_REQUIRE_TIMER; } else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS) { options |= PJSIP_INV_ALWAYS_USE_TIMER; } } } /*ycw-pjsip*/ # if defined(SUPPORT_FAX_T38) && SUPPORT_FAX_T38!=0 if (pjsua_var.enableT38) { options |= PJSIP_INV_ENABLE_T38; } # endif status = pjsip_inv_create_uac( dlg, offer, options, &inv); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Invite session creation failed", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Create Invite session error!"); #if PJ_FAKE_CALL_SUPPORT } #endif goto on_error; } /* Init Session Timers */ # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (NULL == acc) { status = pjsip_timer_init_session(inv, &pjsua_var.ua_cfg.timer_setting #ifdef INCLUDE_TFC_ES , TR_UAC #endif ); } else { # endif status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting #ifdef INCLUDE_TFC_ES , TR_UAC #endif ); # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 } # endif if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Session Timer init failed", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Init session timer error!"); #if PJ_FAKE_CALL_SUPPORT } #endif goto on_error; } /* Create and associate our data in the session. */ call->inv = inv; dlg->mod_data[pjsua_var.mod.id] = call; inv->mod_data[pjsua_var.mod.id] = call; /*ycw-pjsip*/ dlg->callIndex = call_id; dlg->seq = seq; /* Attach user data */ call->user_data = user_data; /* If account is locked to specific transport, then lock dialog * to this transport too. */ if ( # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 acc && # endif acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_dlg_set_transport(dlg, &tp_sel); } /* Set dialog Route-Set: */ #if !defined(INCLUDE_TFC_ES) /*By YuChuwei, For Telefonica*/ if ( # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 acc && # endif !pj_list_empty(&acc->route_set)) { pjsip_dlg_set_route_set(dlg, &acc->route_set); } #endif /* Set credentials: */ if ( # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 acc && # endif acc->cred_cnt) { pjsip_auth_clt_set_credentials( &dlg->auth_sess, acc->cred_cnt, acc->cred); } /* Set authentication preference */ # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 if (acc) { # endif pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref); # if defined(SUPPORT_IPCALL_NO_ACCOUNT) && SUPPORT_IPCALL_NO_ACCOUNT!=0 } # endif #if defined(INCLUDE_SIP_INVITE_PRIORITY) if (other) { inv->isWarmCall = other->isWarmCall; } else { inv->isWarmCall = 0; } #endif /* INCLUDE_SIP_INVITE_PRIORITY */ /* Create initial INVITE: */ status = pjsip_inv_invite(inv, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", status); #if PJ_FAKE_CALL_SUPPORT if (!bfakeCall) { #endif cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "Unable to create initial INVITE request"); #if PJ_FAKE_CALL_SUPPORT } #endif goto on_error; } #ifdef INCLUDE_RFC3325 if (acc) { if (acc->cfg.preferId.slen > 0) { preferedIdHdr = pjsip_p_asserted_identity_hdr_create(tmp_pool); if (NULL == preferedIdHdr) { pjsua_perror(THIS_FILE, "No memory!", PJ_ENOMEM); goto on_error; } if (PJ_TRUE == acc->cfg.supportNAI) { preferedId_Url_Str.ptr = (char*)pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); preferedId_Url_Str.slen = pj_ansi_snprintf(preferedId_Url_Str.ptr, PJSIP_MAX_URL_SIZE, "\"Anonymous\"<sip:%s@%.*s>", acc->cfg.preferId.ptr, (int)acc->srv_domain.slen, pj_strnull(acc->srv_domain.ptr)); } else { preferedId_Url_Str.ptr = (char*)pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); preferedId_Url_Str.slen = pj_ansi_snprintf(preferedId_Url_Str.ptr, PJSIP_MAX_URL_SIZE, "\"%s\"<sip:%s@%.*s>", acc->cfg.preferId.ptr, acc->cfg.preferId.ptr, (int)acc->srv_domain.slen, pj_strnull(acc->srv_domain.ptr)); } CMSIP_PRINT("==parse prefered identity=="); preferedIdUri = (pjsip_uri*)pjsip_parse_uri(tmp_pool, preferedId_Url_Str.ptr, preferedId_Url_Str.slen, 0); if (NULL == preferedIdUri) { CMSIP_PRINT("==parse prefered identity error=="); pjsua_perror(THIS_FILE, "parse value of Prefered Identity error ", PJ_EINVAL); } else { preferedIdHdr->uri = preferedIdUri; pj_list_push_back(&msg_data->hdr_list, preferedIdHdr); } } hname = pj_str("Privacy"); if(acc->cfg.supportNAI) { hvalue = pj_str("id"); } else { hvalue = pj_str("none"); } pjsip_generic_string_hdr_init2(&privacyHdr, &hname, &hvalue); pj_list_push_back(&msg_data->hdr_list, &privacyHdr); } #endif /* INCLUDE_RFC3325 */ #if defined(INCLUDE_ROBIN_DNS) || defined(INCLUDE_VOIP_ROBIN_DNS) if (acc && acc->regc && acc->regc->srv_ipinfo.server_name.slen) { CMSIP_PRINT("==copy server address from regc to dlg=="); pj_strdup(dlg->pool, &dlg->server_name, &acc->regc->srv_ipinfo.server_name); memcpy(&dlg->server_addr, &acc->regc->srv_ipinfo.server_addr, sizeof(dlg->server_addr) ); dlg->cur_addr = acc->regc->srv_ipinfo.cur_addr; } #endif /* INCLUDE_ROBIN_DNS || INCLUDE_VOIP_ROBIN_DNS */ /* Add additional headers etc */ pjsua_process_msg_data( tdata, msg_data); # if PJ_FAKE_CALL_SUPPORT if (bfakeCall) { char contact[1][MAX_URI_LEN] = {{0}}; cmsip_send_callStatus(CMSIP_CALL_GENERIC, call_id, seq, 0, contact, 1); status = PJ_EINVAL; goto on_error; } # endif /* Send initial INVITE: */ status = pjsip_inv_send_msg(inv, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", status); cmsip_send_systemlog(CMSIP_SYSTEMLOG_WARN, "Unable to send initial INVITE request for [accIndex(%d),callIndex(%d) dest(%.*s)]", acc_id, call_id, dest_uri->slen, pj_strnull(dest_uri->ptr)); /* Upon failure to send first request, the invite * session would have been cleared. */ inv = NULL; goto on_error; } cmsip_send_systemlog(CMSIP_SYSTEMLOG_DEBUG, "the initial INVITE request for [accIndex(%d),callIndex(%d) dest(%.*s)] is sent", acc_id, call_id, dest_uri->slen, pj_strnull(dest_uri->ptr)); /* Must increment call counter now */ ++pjsua_var.call_cnt; CMSIP_PRINT("~~~~now, call count is (%d)~~~~~", pjsua_var.call_cnt); #if defined(INCLUDE_TFC_ES) && PJ_RFC3960_SUPPORT #if PJ_MEDIA_TRANSIT_BY_PJSIP==0 pjmedia_transport_udp_local_info(call->med_orig, localIp, &rtpPort, &rtcpPort, &family); cmsip_send_rtpcreate(call->index, (options==pj_call_t38)?CMSIP_MEDIA_TYPE_IMAGE:CMSIP_MEDIA_TYPE_AUDIO, (options==pj_call_t38)?CMSIP_MEDIA_TPPROTO_UDPTL:CMSIP_MEDIA_TPPROTO_RTP_AVP, CMSIP_MEDIA_DIR_NONE, NULL, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, localIp, strlen(localIp), rtpPort, NULL, 0, 0, 0, family); #else /*BTD*/ #endif #endif /* Done. */ if (p_call_id) { *p_call_id = call_id; } pjsip_dlg_dec_lock(dlg); pj_pool_release(tmp_pool); PJSUA_UNLOCK(); return PJ_SUCCESS; on_error: /*If tdata has been created, must release it, or , this will cause memory leak. By Yuchuwei*/ if (tdata) { pjsip_tx_data_dec_ref(tdata); tdata = NULL; } if (dlg) { /* This may destroy the dialog */ pjsip_dlg_dec_lock(dlg); } if (inv != NULL) { pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE); } if (call_id != -1) { pjsua_media_channel_deinit(call_id); reset_call(call_id); # if PJ_FAKE_CALL_SUPPORT if (bfakeCall) { call->busy = PJ_FALSE; } # endif } CMSIP_PRINT("==release pool=="); pj_pool_release(tmp_pool); PJSUA_UNLOCK(); return status; }
最新发布
10-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值