Changeset 4175


Ignore:
Timestamp:
Jun 22, 2012 8:53:11 AM (7 years ago)
Author:
nanang
Message:

Fix #1526:

  • Fix incoming call without SDP by delaying media init in on_incoming_call() until call setting is set i.e: via call answer. Note that there is no need to delay the media init in the case of call replace request, the call setting is already set in the incoming call callback, i.e: via on_call_replace_request2() callback or just the current setting of the replaced call.
  • Fix processing call replace request when the media init is performed asynchronously.
Location:
pjproject/trunk/pjsip
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h

    r4173 r4175  
    40854085 * Send response to incoming INVITE request. Depending on the status 
    40864086 * code specified as parameter, this function may send provisional 
    4087  * response, establish the call, or terminate the call. 
     4087 * response, establish the call, or terminate the call. See also 
     4088 * #pjsua_call_answer2(). 
    40884089 * 
    40894090 * @param call_id       Incoming call identification. 
     
    41034104 
    41044105/** 
    4105  * Send response to incoming INVITE request. Depending on the status 
    4106  * code specified as parameter, this function may send provisional 
    4107  * response, establish the call, or terminate the call. 
     4106 * Send response to incoming INVITE request with call setting param. 
     4107 * Depending on the status code specified as parameter, this function may 
     4108 * send provisional response, establish the call, or terminate the call. 
     4109 * Notes about call setting: 
     4110 *  - if call setting is changed in the subsequent call to this function, 
     4111 *    only the first call setting supplied will applied. So normally 
     4112 *    application will not supply call setting before getting confirmation 
     4113 *    from the user. 
     4114 *  - if no call setting is supplied when SDP has to be sent, i.e: answer 
     4115 *    with status code 183 or 2xx, the default call setting will be used, 
     4116 *    check #pjsua_call_setting for its default values. 
    41084117 * 
    41094118 * @param call_id       Incoming call identification. 
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h

    r4173 r4175  
    109109    pj_str_t        *reason;        /**< Answer's reason phrase.      */ 
    110110    unsigned         code;          /**< Answer's status code.        */ 
     111    pjsua_call_setting *opt;        /**< Answer's call setting.       */ 
    111112} call_answer; 
    112113 
     
    121122    unsigned             index;     /**< Index in pjsua array.              */ 
    122123    pjsua_call_setting   opt;       /**< Call setting.                      */ 
     124    pj_bool_t            opt_inited;/**< Initial call setting has been set, 
     125                                         to avoid different opt in answer.  */ 
    123126    pjsip_inv_session   *inv;       /**< The invite session.                */ 
    124127    void                *user_data; /**< User/application data.             */ 
     
    171174            struct { 
    172175                call_answer      answers;/**< A list of call answers.       */ 
     176                pjsip_dialog    *replaced_dlg; /**< Replaced dialog.        */ 
    173177            } inc_call; 
    174178        } call_var; 
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c

    r4173 r4175  
    807807 
    808808 
     809static pj_status_t process_incoming_call_replace(pjsua_call *call, 
     810                                                 pjsip_dialog *replaced_dlg) 
     811{ 
     812    pjsip_inv_session *replaced_inv; 
     813    struct pjsua_call *replaced_call; 
     814    pjsip_tx_data *tdata; 
     815    pj_status_t status; 
     816 
     817    /* Get the invite session in the dialog */ 
     818    replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg); 
     819 
     820    /* Get the replaced call instance */ 
     821    replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id]; 
     822 
     823    /* Notify application */ 
     824    if (pjsua_var.ua_cfg.cb.on_call_replaced) 
     825        pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index, 
     826                                             call->index); 
     827 
     828    PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK", 
     829                         call->index)); 
     830 
     831    /* Answer the new call with 200 response */ 
     832    status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); 
     833    if (status == PJ_SUCCESS) 
     834        status = pjsip_inv_send_msg(call->inv, tdata); 
     835 
     836    if (status != PJ_SUCCESS) 
     837        pjsua_perror(THIS_FILE, "Error answering session", status); 
     838 
     839    /* Note that inv may be invalid if 200/OK has caused error in 
     840     * starting the media. 
     841     */ 
     842 
     843    PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d", 
     844                         replaced_call->index)); 
     845 
     846    /* Disconnect replaced invite session */ 
     847    status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, 
     848                                   &tdata); 
     849    if (status == PJ_SUCCESS && tdata) 
     850        status = pjsip_inv_send_msg(replaced_inv, tdata); 
     851 
     852    if (status != PJ_SUCCESS) 
     853        pjsua_perror(THIS_FILE, "Error terminating session", status); 
     854 
     855    return status; 
     856} 
     857 
     858 
     859static void process_pending_call_answer(pjsua_call *call) 
     860{ 
     861    struct call_answer *answer, *next; 
     862     
     863    answer = call->async_call.call_var.inc_call.answers.next; 
     864    while (answer != &call->async_call.call_var.inc_call.answers) { 
     865        next = answer->next; 
     866        pjsua_call_answer2(call->index, answer->opt, answer->code, 
     867                           answer->reason, answer->msg_data); 
     868         
     869        /* Call might have been disconnected if application is answering 
     870         * with 200/OK and the media failed to start. 
     871         * See pjsua_call_answer() below. 
     872         */ 
     873        if (!call->inv || !call->inv->pool_prov) 
     874            break; 
     875 
     876        pj_list_erase(answer); 
     877        answer = next; 
     878    } 
     879} 
     880 
     881 
    809882/* Incoming call callback when media transport creation is completed. */ 
    810883static pj_status_t 
     
    890963    call->med_ch_cb = NULL; 
    891964 
    892     if (status == PJ_SUCCESS && 
    893         !pj_list_empty(&call->async_call.call_var.inc_call.answers)) 
    894     { 
    895         struct call_answer *answer, *next; 
    896          
    897         answer = call->async_call.call_var.inc_call.answers.next; 
    898         while (answer != &call->async_call.call_var.inc_call.answers) { 
    899             next = answer->next; 
    900             pjsua_call_answer(call_id, answer->code, answer->reason, 
    901                               answer->msg_data); 
    902              
    903             /* Call might have been disconnected if application is answering 
    904              * with 200/OK and the media failed to start. 
    905              * See pjsua_call_answer() below. 
    906              */ 
    907             if (!call->inv || !call->inv->pool_prov) 
    908                 break; 
    909  
    910             pj_list_erase(answer); 
    911             answer = next; 
    912         } 
     965    /* Finish any pending process */ 
     966    if (status == PJ_SUCCESS) { 
     967        if (call->async_call.call_var.inc_call.replaced_dlg) { 
     968            /* Process pending call replace */ 
     969            pjsip_dialog *replaced_dlg =  
     970                        call->async_call.call_var.inc_call.replaced_dlg; 
     971            process_incoming_call_replace(call, replaced_dlg); 
     972        } else { 
     973            /* Process pending call answers */ 
     974            process_pending_call_answer(call); 
     975        } 
    913976    } 
    914977 
     
    12531316    pj_list_init(&call->async_call.call_var.inc_call.answers); 
    12541317 
    1255     /* Init media channel */ 
    1256     status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,  
    1257                                       call->secure_level,  
    1258                                       rdata->tp_info.pool, 
    1259                                       offer, 
    1260                                       &sip_err_code, PJ_TRUE, 
    1261                                       &on_incoming_call_med_tp_complete); 
    1262     if (status == PJ_SUCCESS) { 
    1263         status = on_incoming_call_med_tp_complete(call_id, NULL); 
    1264         if (status != PJ_SUCCESS) { 
    1265             sip_err_code = PJSIP_SC_NOT_ACCEPTABLE; 
    1266             /* Since the call invite's state is still PJSIP_INV_STATE_NULL, 
    1267              * the invite session was not ended in 
    1268              * on_incoming_call_med_tp_complete(), so we need to send 
    1269              * a response message and terminate the invite here. 
    1270              */ 
    1271             pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL); 
    1272             pjsip_inv_terminate(call->inv, sip_err_code, PJ_FALSE);  
    1273             call->inv = NULL;  
     1318    /* Init media channel, only when there is offer or call replace request. 
     1319     * For incoming call without SDP offer, media channel init will be done 
     1320     * in pjsua_call_answer(), see ticket #1526. 
     1321     */ 
     1322    if (offer || replaced_dlg) { 
     1323        status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,  
     1324                                          call->secure_level,  
     1325                                          rdata->tp_info.pool, 
     1326                                          offer, 
     1327                                          &sip_err_code, PJ_TRUE, 
     1328                                          &on_incoming_call_med_tp_complete); 
     1329        if (status == PJ_SUCCESS) { 
     1330            status = on_incoming_call_med_tp_complete(call_id, NULL); 
     1331            if (status != PJ_SUCCESS) { 
     1332                sip_err_code = PJSIP_SC_NOT_ACCEPTABLE; 
     1333                /* Since the call invite's state is still PJSIP_INV_STATE_NULL, 
     1334                 * the invite session was not ended in 
     1335                 * on_incoming_call_med_tp_complete(), so we need to send 
     1336                 * a response message and terminate the invite here. 
     1337                 */ 
     1338                pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL); 
     1339                pjsip_inv_terminate(call->inv, sip_err_code, PJ_FALSE);  
     1340                call->inv = NULL;  
     1341                goto on_return; 
     1342            } 
     1343        } else if (status != PJ_EPENDING) { 
     1344            pjsua_perror(THIS_FILE, "Error initializing media channel", status); 
     1345            pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL); 
     1346            pjsip_inv_terminate(call->inv, sip_err_code, PJ_FALSE);  
     1347            call->inv = NULL;  
    12741348            goto on_return; 
    1275         } 
    1276     } else if (status != PJ_EPENDING) { 
    1277         pjsua_perror(THIS_FILE, "Error initializing media channel", status); 
    1278         pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL); 
    1279         pjsip_inv_terminate(call->inv, sip_err_code, PJ_FALSE);  
    1280         call->inv = NULL;  
    1281         goto on_return; 
     1349        } 
    12821350    } 
    12831351 
     
    13651433    /* Check if this request should replace existing call */ 
    13661434    if (replaced_dlg) { 
    1367         pjsip_inv_session *replaced_inv; 
    1368         struct pjsua_call *replaced_call; 
    1369         pjsip_tx_data *tdata; 
    1370  
    1371         /* Get the invite session in the dialog */ 
    1372         replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg); 
    1373  
    1374         /* Get the replaced call instance */ 
    1375         replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id]; 
    1376  
    1377         /* Notify application */ 
    1378         if (pjsua_var.ua_cfg.cb.on_call_replaced) 
    1379             pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index, 
    1380                                                  call_id); 
    1381  
    1382         PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK", 
    1383                              call_id)); 
    1384  
    1385         /* Answer the new call with 200 response */ 
    1386         status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata); 
    1387         if (status == PJ_SUCCESS) 
    1388             status = pjsip_inv_send_msg(inv, tdata); 
    1389  
    1390         if (status != PJ_SUCCESS) 
    1391             pjsua_perror(THIS_FILE, "Error answering session", status); 
    1392  
    1393         /* Note that inv may be invalid if 200/OK has caused error in 
    1394          * starting the media. 
     1435        /* Process call replace. If the media channel init has been completed, 
     1436         * just process now, otherwise, just queue the replaced dialog so 
     1437         * it will be processed once the media channel async init is finished 
     1438         * successfully. 
    13951439         */ 
    1396  
    1397         PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d", 
    1398                              replaced_call->index)); 
    1399  
    1400         /* Disconnect replaced invite session */ 
    1401         status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, 
    1402                                        &tdata); 
    1403         if (status == PJ_SUCCESS && tdata) 
    1404             status = pjsip_inv_send_msg(replaced_inv, tdata); 
    1405  
    1406         if (status != PJ_SUCCESS) 
    1407             pjsua_perror(THIS_FILE, "Error terminating session", status); 
    1408  
    1409  
     1440        if (call->med_ch_cb == NULL) { 
     1441            process_incoming_call_replace(call, replaced_dlg); 
     1442        } else { 
     1443            call->async_call.call_var.inc_call.replaced_dlg = replaced_dlg; 
     1444        } 
    14101445    } else { 
    1411  
    14121446        /* Notify application if on_incoming_call() is overriden,  
    14131447         * otherwise hangup the call with 480 
     
    18221856 
    18231857 
     1858/* Media channel init callback for pjsua_call_answer(). */ 
     1859static pj_status_t 
     1860on_answer_call_med_tp_complete(pjsua_call_id call_id, 
     1861                               const pjsua_med_tp_state_info *info) 
     1862{ 
     1863    pjsua_call *call = &pjsua_var.calls[call_id]; 
     1864    pjmedia_sdp_session *sdp; 
     1865    int sip_err_code = (info? info->sip_err_code: 0); 
     1866    pj_status_t status = (info? info->status: PJ_SUCCESS); 
     1867 
     1868    PJSUA_LOCK(); 
     1869 
     1870    if (status != PJ_SUCCESS) { 
     1871        pjsua_perror(THIS_FILE, "Error initializing media channel", status); 
     1872        goto on_return; 
     1873    } 
     1874 
     1875    /* pjsua_media_channel_deinit() has been called. */ 
     1876    if (call->async_call.med_ch_deinit) { 
     1877        pjsua_media_channel_deinit(call->index); 
     1878        call->med_ch_cb = NULL; 
     1879        PJSUA_UNLOCK(); 
     1880        return PJ_SUCCESS; 
     1881    } 
     1882 
     1883    status = pjsua_media_channel_create_sdp(call_id, 
     1884                                            call->async_call.dlg->pool,  
     1885                                            NULL, &sdp, &sip_err_code); 
     1886    if (status != PJ_SUCCESS) { 
     1887        pjsua_perror(THIS_FILE, "Error creating SDP answer", status); 
     1888        goto on_return; 
     1889    } 
     1890 
     1891    status = pjsip_inv_set_local_sdp(call->inv, sdp); 
     1892    if (status != PJ_SUCCESS) { 
     1893        pjsua_perror(THIS_FILE, "Error setting local SDP", status); 
     1894        sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE; 
     1895        goto on_return; 
     1896    } 
     1897 
     1898on_return: 
     1899    if (status != PJ_SUCCESS) { 
     1900        /* If the callback is called from pjsua_call_on_incoming(), the 
     1901         * invite's state is PJSIP_INV_STATE_NULL, so the invite session 
     1902         * will be terminated later, otherwise we end the session here. 
     1903         */ 
     1904        if (call->inv->state > PJSIP_INV_STATE_NULL) { 
     1905            pjsip_tx_data *tdata; 
     1906            pj_status_t status_; 
     1907 
     1908            status_ = pjsip_inv_end_session(call->inv, sip_err_code, NULL, 
     1909                                            &tdata); 
     1910            if (status_ == PJ_SUCCESS && tdata) 
     1911                status_ = pjsip_inv_send_msg(call->inv, tdata); 
     1912        } 
     1913 
     1914        pjsua_media_channel_deinit(call->index); 
     1915    } 
     1916 
     1917    /* Set the callback to NULL to indicate that the async operation 
     1918     * has completed. 
     1919     */ 
     1920    call->med_ch_cb = NULL; 
     1921 
     1922    /* Finish any pending process */ 
     1923    if (status == PJ_SUCCESS) { 
     1924        /* Process pending call answers */ 
     1925        process_pending_call_answer(call); 
     1926    } 
     1927 
     1928    PJSUA_UNLOCK(); 
     1929    return status; 
     1930} 
     1931 
     1932 
    18241933/* 
    18251934 * Send response to incoming INVITE request. 
     
    18581967        goto on_return; 
    18591968 
    1860     /* Apply call setting */ 
    1861     status = apply_call_setting(call, opt, NULL); 
    1862     if (status != PJ_SUCCESS) { 
    1863         pjsua_perror(THIS_FILE, "Failed to apply call setting", status); 
    1864         goto on_return; 
     1969    /* Apply call setting, only if status code is 1xx or 2xx. */ 
     1970    if (opt && code < 300) { 
     1971        /* Check if it has not been set previously or it is different to 
     1972         * the previous one. 
     1973         */ 
     1974        if (!call->opt_inited) { 
     1975            call->opt_inited = PJ_TRUE; 
     1976            apply_call_setting(call, opt, NULL); 
     1977        } else if (pj_memcmp(opt, &call->opt, sizeof(*opt)) != 0) { 
     1978            /* Warn application about call setting inconsistency */ 
     1979            PJ_LOG(2,(THIS_FILE, "The call setting changes is ignored.")); 
     1980        } 
    18651981    } 
    18661982 
    18671983    PJSUA_LOCK(); 
     1984 
     1985    /* Ticket #1526: When the incoming call contains no SDP offer, the media 
     1986     * channel may have not been initialized at this stage. The media channel 
     1987     * will be initialized here (along with SDP local offer generation) when 
     1988     * the following conditions are met: 
     1989     * - no pending media channel init 
     1990     * - local SDP has not been generated 
     1991     * - call setting has just been set, or SDP offer needs to be sent, i.e: 
     1992     *   answer code 183 or 2xx is issued 
     1993     */ 
     1994    if (!call->med_ch_cb &&  
     1995        (call->opt_inited || (code==183 && code/100==2)) && 
     1996        (!call->inv->neg || 
     1997         pjmedia_sdp_neg_get_state(call->inv->neg) ==  
     1998                PJMEDIA_SDP_NEG_STATE_NULL)) 
     1999    { 
     2000        /* Mark call setting as initialized as it is just about to be used 
     2001         * for initializing the media channel. 
     2002         */ 
     2003        call->opt_inited = PJ_TRUE; 
     2004 
     2005        status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, 
     2006                                          call->secure_level, 
     2007                                          dlg->pool, 
     2008                                          NULL, NULL, PJ_TRUE, 
     2009                                          &on_answer_call_med_tp_complete); 
     2010        if (status == PJ_SUCCESS) { 
     2011            status = on_answer_call_med_tp_complete(call->index, NULL); 
     2012            if (status != PJ_SUCCESS) 
     2013                goto on_return; 
     2014        } else if (status != PJ_EPENDING) { 
     2015            pjsua_perror(THIS_FILE, "Error initializing media channel", status); 
     2016            goto on_return; 
     2017        } 
     2018    } 
     2019 
    18682020    /* If media transport creation is not yet completed, we will answer 
    18692021     * the call in the media transport creation callback instead. 
     
    18772029        answer = PJ_POOL_ZALLOC_T(call->inv->pool_prov, struct call_answer); 
    18782030        answer->code = code; 
     2031        if (opt) { 
     2032            answer->opt = PJ_POOL_ZALLOC_T(call->inv->pool_prov, 
     2033                                           pjsua_call_setting); 
     2034            *answer->opt = *opt; 
     2035        } 
    18792036        if (reason) { 
    18802037            pj_strdup(call->inv->pool_prov, answer->reason, reason); 
     
    18922049        return status; 
    18932050    } 
     2051 
    18942052    PJSUA_UNLOCK(); 
    18952053 
Note: See TracChangeset for help on using the changeset viewer.