sipcall.cpp 43.6 KB
Newer Older
yanmorin's avatar
yanmorin committed
1
/*
2
 *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
3
 *
4
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
5
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
jpbl's avatar
jpbl committed
6
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
7 8
 *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
 *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
9
 *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
jpbl's avatar
jpbl committed
10
 *
yanmorin's avatar
yanmorin committed
11 12
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
yanmorin's avatar
yanmorin committed
14
 *  (at your option) any later version.
jpbl's avatar
jpbl committed
15
 *
yanmorin's avatar
yanmorin committed
16 17 18 19
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
jpbl's avatar
jpbl committed
20
 *
yanmorin's avatar
yanmorin committed
21 22
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
23
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
jpbl's avatar
jpbl committed
24 25
 */

26
#include "call_factory.h"
yanmorin's avatar
yanmorin committed
27
#include "sipcall.h"
28 29
#include "sipaccount.h" // for SIPAccount::ACCOUNT_TYPE
#include "sipaccountbase.h"
30
#include "sipvoiplink.h"
31
#include "logger.h" // for _debug
32
#include "sdp.h"
33
#include "manager.h"
34
#include "string_utils.h"
35
#include "upnp/upnp_control.h"
36
#include "sip_utils.h"
Guillaume Roguez's avatar
Guillaume Roguez committed
37
#include "audio/audio_rtp_session.h"
38
#include "system_codec_container.h"
39
#include "im/instant_messaging.h"
40
#include "dring/call_const.h"
41
#include "dring/media_const.h"
42
#include "client/ring_signal.h"
43
#include "ice_transport.h"
44

Adrien Béraud's avatar
Adrien Béraud committed
45
#ifdef ENABLE_VIDEO
46
#include "client/videomanager.h"
47
#include "video/video_rtp_session.h"
48
#include "dring/videomanager_interface.h"
49
#include <chrono>
50 51
#endif

52 53
#include "errno.h"

54
#include <opendht/crypto.h>
Adrien Béraud's avatar
Adrien Béraud committed
55
#include <opendht/thread_pool.h>
56

Adrien Béraud's avatar
Adrien Béraud committed
57
namespace jami {
58

59 60
using sip_utils::CONST_PJ_STR;

Adrien Béraud's avatar
Adrien Béraud committed
61
#ifdef ENABLE_VIDEO
Guillaume Roguez's avatar
Guillaume Roguez committed
62 63 64
static DeviceParams
getVideoSettings()
{
Adrien Béraud's avatar
Adrien Béraud committed
65
    const auto& videomon = jami::getVideoDeviceMonitor();
66
    return videomon.getDeviceParams(videomon.getDefaultDevice());
Guillaume Roguez's avatar
Guillaume Roguez committed
67 68 69
}
#endif

70 71
static constexpr std::chrono::seconds DEFAULT_ICE_INIT_TIMEOUT {35}; // seconds
static constexpr std::chrono::seconds DEFAULT_ICE_NEGO_TIMEOUT {60}; // seconds
72 73 74 75 76 77 78 79 80 81 82

// SDP media Ids
static constexpr int SDP_AUDIO_MEDIA_ID {0};
static constexpr int SDP_VIDEO_MEDIA_ID {1};

// ICE components Id used on SIP
static constexpr int ICE_AUDIO_RTP_COMPID {0};
static constexpr int ICE_AUDIO_RTCP_COMPID {1};
static constexpr int ICE_VIDEO_RTP_COMPID {2};
static constexpr int ICE_VIDEO_RTCP_COMPID {3};

83 84
const char* const SIPCall::LINK_TYPE = SIPAccount::ACCOUNT_TYPE;

85 86 87
SIPCall::SIPCall(SIPAccountBase& account, const std::string& id, Call::CallType type,
                 const std::map<std::string, std::string>& details)
    : Call(account, id, type, details)
Guillaume Roguez's avatar
Guillaume Roguez committed
88
    , avformatrtp_(new AudioRtpSession(id))
Adrien Béraud's avatar
Adrien Béraud committed
89
#ifdef ENABLE_VIDEO
90
    // The ID is used to associate video streams to calls
91
    , videortp_(new video::VideoRtpSession(id, getVideoSettings()))
92
    , mediaInput_(Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice())
93
#endif
94
    , sdp_(new Sdp(id))
95
{
96
    if (account.getUPnPActive())
97
        upnp_.reset(new upnp::Controller());
98 99

    setCallMediaLocal();
100
}
yanmorin's avatar
yanmorin committed
101

Emmanuel Milou's avatar
Emmanuel Milou committed
102 103
SIPCall::~SIPCall()
{
104
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
105
    setTransport({});
106
    inv.reset(); // prevents callback usage
jpbl's avatar
jpbl committed
107
}
108

109
SIPAccountBase&
110 111
SIPCall::getSIPAccount() const
{
112
    return static_cast<SIPAccountBase&>(getAccount());
113 114 115
}

void
116
SIPCall::setCallMediaLocal()
117
{
118
    if (localAudioPort_ == 0
Adrien Béraud's avatar
Adrien Béraud committed
119
#ifdef ENABLE_VIDEO
120
        || localVideoPort_ == 0
121 122 123 124 125 126 127
#endif
        )
        generateMediaPorts();
}

void
SIPCall::generateMediaPorts()
128 129 130 131 132
{
    auto& account = getSIPAccount();

    // Reference: http://www.cs.columbia.edu/~hgs/rtp/faq.html#ports
    // We only want to set ports to new values if they haven't been set
133
    const unsigned callLocalAudioPort = account.generateAudioPort();
134 135 136
    if (localAudioPort_ != 0)
        account.releasePort(localAudioPort_);
    localAudioPort_ = callLocalAudioPort;
137
    sdp_->setLocalPublishedAudioPort(callLocalAudioPort);
138

Adrien Béraud's avatar
Adrien Béraud committed
139
#ifdef ENABLE_VIDEO
140 141
    // https://projects.savoirfairelinux.com/issues/17498
    const unsigned int callLocalVideoPort = account.generateVideoPort();
142 143
    if (localVideoPort_ != 0)
        account.releasePort(localVideoPort_);
144
    // this should already be guaranteed by SIPAccount
145 146
    assert(localAudioPort_ != callLocalVideoPort);
    localVideoPort_ = callLocalVideoPort;
147
    sdp_->setLocalPublishedVideoPort(callLocalVideoPort);
148 149 150
#endif
}

151
void SIPCall::setContactHeader(pj_str_t *contact)
152
{
153
    pj_strcpy(&contactHeader_, contact);
154 155
}

156
void
157
SIPCall::setTransport(const std::shared_ptr<SipTransport>& t)
158
{
159
    if (isSecure() and t and not t->isSecure()) {
Adrien Béraud's avatar
Adrien Béraud committed
160
        JAMI_ERR("Can't set unsecure transport to secure call.");
161 162 163
        return;
    }

164 165 166 167 168 169
    const auto list_id = reinterpret_cast<uintptr_t>(this);
    if (transport_)
        transport_->removeStateListener(list_id);
    transport_ = t;

    if (transport_) {
170
        setSecure(transport_->isSecure());
171 172 173

        // listen for transport destruction
        transport_->addStateListener(list_id,
Adrien Béraud's avatar
Adrien Béraud committed
174
            [wthis_ = weak()] (pjsip_transport_state state, const pjsip_transport_state_info*) {
175 176
                if (auto this_ = wthis_.lock()) {
                    // end the call if the SIP transport is shut down
177
                    if (not SipTransport::isAlive(this_->transport_, state) and this_->getConnectionState() != ConnectionState::DISCONNECTED) {
Adrien Béraud's avatar
Adrien Béraud committed
178
                        JAMI_WARN("[call:%s] Ending call because underlying SIP transport was closed",
179
                                  this_->getCallId().c_str());
180
                        this_->onFailure(ECONNRESET);
181
                    }
182
                }
183 184 185 186
            });
    }
}

187 188 189 190 191
/**
 * Send a reINVITE inside an active dialog to modify its state
 * Local SDP session should be modified before calling this method
 */

192 193
int
SIPCall::SIPSessionReinvite()
194
{
195
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
196
    // Do nothing if no invitation processed yet
197
    if (not inv or inv->invite_tsx)
198 199
        return PJ_SUCCESS;

Adrien Béraud's avatar
Adrien Béraud committed
200
    JAMI_DBG("[call:%s] Processing reINVITE (state=%s)", getCallId().c_str(),
201 202
             pjsip_inv_state_name(inv->state));

203 204 205 206 207
    // Generate new ports to receive the new media stream
    // LibAV doesn't discriminate SSRCs and will be confused about Seq changes on a given port
    generateMediaPorts();
    sdp_->clearIce();
    auto& acc = getSIPAccount();
208
    if (not sdp_->createOffer(acc.getActiveAccountCodecInfoList(MEDIA_AUDIO),
209 210
                              acc.getActiveAccountCodecInfoList(acc.isVideoEnabled() and not isAudioOnly() ? MEDIA_VIDEO
                                                                                                           : MEDIA_NONE),
211 212 213 214
                              acc.getSrtpKeyExchange(),
                              getState() == CallState::HOLD))
        return !PJ_SUCCESS;

215
    if (initIceMediaTransport(true))
216 217
        setupLocalSDPFromIce();

218 219 220 221 222
    pjsip_tx_data* tdata;
    auto local_sdp = sdp_->getLocalSdpSession();
    auto result = pjsip_inv_reinvite(inv.get(), nullptr, local_sdp, &tdata);
    if (result == PJ_SUCCESS) {
        if (!tdata)
223
            return PJ_SUCCESS;
224 225 226
        result = pjsip_inv_send_msg(inv.get(), tdata);
        if (result == PJ_SUCCESS)
            return PJ_SUCCESS;
Adrien Béraud's avatar
Adrien Béraud committed
227
        JAMI_ERR("[call:%s] Failed to send REINVITE msg (pjsip: %s)", getCallId().c_str(),
228 229 230 231
                 sip_utils::sip_strerror(result).c_str());
        // Canceling internals without sending (anyways the send has just failed!)
        pjsip_inv_cancel_reinvite(inv.get(), &tdata);
    } else
Adrien Béraud's avatar
Adrien Béraud committed
232
        JAMI_ERR("[call:%s] Failed to create REINVITE msg (pjsip: %s)", getCallId().c_str(),
233
                 sip_utils::sip_strerror(result).c_str());
234 235 236 237

    return !PJ_SUCCESS;
}

238 239 240
void
SIPCall::sendSIPInfo(const char *const body, const char *const subtype)
{
241
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
242 243 244
    if (not inv or not inv->dlg)
        throw VoipLinkException("Couldn't get invite dialog");

245
    pj_str_t methodName = CONST_PJ_STR("INFO");
246
    constexpr pj_str_t type = CONST_PJ_STR("application");
247

248 249 250 251 252 253 254
    pjsip_method method;
    pjsip_method_init_np(&method, &methodName);

    /* Create request message. */
    pjsip_tx_data *tdata;

    if (pjsip_dlg_create_request(inv->dlg, &method, -1, &tdata) != PJ_SUCCESS) {
Adrien Béraud's avatar
Adrien Béraud committed
255
        JAMI_ERR("[call:%s] Could not create dialog", getCallId().c_str());
256 257 258 259 260 261 262 263 264
        return;
    }

    /* Create "application/<subtype>" message body. */
    pj_str_t content;
    pj_cstr(&content, body);
    pj_str_t pj_subtype;
    pj_cstr(&pj_subtype, subtype);
    tdata->msg->body = pjsip_msg_body_create(tdata->pool, &type, &pj_subtype, &content);
265
    if (tdata->msg->body == NULL)
266
        pjsip_tx_data_dec_ref(tdata);
267 268
    else
        pjsip_dlg_send_request(inv->dlg, tdata, getSIPVoIPLink()->getModId(), NULL);
269 270
}

271 272 273
void
SIPCall::requestKeyframe()
{
274
    constexpr const char * const BODY =
275 276 277 278 279
        "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
        "<media_control><vc_primitive><to_encoder>"
        "<picture_fast_update/>"
        "</to_encoder></vc_primitive></media_control>";

Adrien Béraud's avatar
Adrien Béraud committed
280
    JAMI_DBG("Sending video keyframe request via SIP INFO");
281 282 283
    try {
        sendSIPInfo(BODY, "media_control+xml");
    } catch (const std::exception& e) {
Adrien Béraud's avatar
Adrien Béraud committed
284
        JAMI_ERR("Error sending video keyframe request: %s", e.what());
285 286 287
    }
}

288 289
void
SIPCall::updateSDPFromSTUN()
290
{
Adrien Béraud's avatar
Adrien Béraud committed
291
    JAMI_WARN("[call:%s] SIPCall::updateSDPFromSTUN() not implemented", getCallId().c_str());
292 293
}

294 295 296
void
SIPCall::terminateSipSession(int status)
{
Adrien Béraud's avatar
Adrien Béraud committed
297
    JAMI_DBG("[call:%s] Terminate SIP session", getCallId().c_str());
298
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
299 300 301 302 303 304 305 306 307
    if (inv and inv->state != PJSIP_INV_STATE_DISCONNECTED) {
        pjsip_tx_data* tdata = nullptr;
        auto ret = pjsip_inv_end_session(inv.get(), status, nullptr, &tdata);
        if (ret == PJ_SUCCESS) {
            if (tdata) {
                auto contact = getSIPAccount().getContactHeader(transport_ ? transport_->get() : nullptr);
                sip_utils::addContactHeader(&contact, tdata);
                ret = pjsip_inv_send_msg(inv.get(), tdata);
                if (ret != PJ_SUCCESS)
Adrien Béraud's avatar
Adrien Béraud committed
308
                    JAMI_ERR("[call:%s] failed to send terminate msg, SIP error (%s)",
309 310 311
                             getCallId().c_str(), sip_utils::sip_strerror(ret).c_str());
            }
        } else
Adrien Béraud's avatar
Adrien Béraud committed
312
            JAMI_ERR("[call:%s] failed to terminate INVITE@%p, SIP error (%s)",
313 314 315 316 317 318 319 320
                     getCallId().c_str(), inv.get(), sip_utils::sip_strerror(ret).c_str());
    }

    inv.reset();
}

void
SIPCall::answer()
321
{
322
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
323
    auto& account = getSIPAccount();
324

325
    if (not inv)
326
        throw VoipLinkException("[call:" + getCallId() + "] answer: no invite session for this call");
327

328
    if (!inv->neg) {
Adrien Béraud's avatar
Adrien Béraud committed
329
        JAMI_WARN("[call:%s] Negotiator is NULL, we've received an INVITE without an SDP",
330
                  getCallId().c_str());
331
        pjmedia_sdp_session *dummy = 0;
332
        getSIPVoIPLink()->createSDPOffer(inv.get(), &dummy);
333

334 335
        if (account.isStunEnabled())
            updateSDPFromSTUN();
336 337
    }

338
    pj_str_t contact(account.getContactHeader(transport_ ? transport_->get() : nullptr));
339 340 341 342 343 344 345
    setContactHeader(&contact);

    pjsip_tx_data *tdata;
    if (!inv->last_answer)
        throw std::runtime_error("Should only be called for initial answer");

    // answer with SDP if no SDP was given in initial invite (i.e. inv->neg is NULL)
346
    if (pjsip_inv_answer(inv.get(), PJSIP_SC_OK, NULL, !inv->neg ? sdp_->getLocalSdpSession() : NULL, &tdata) != PJ_SUCCESS)
347 348 349 350
        throw std::runtime_error("Could not init invite request answer (200 OK)");

    // contactStr must stay in scope as long as tdata
    if (contactHeader_.slen) {
Adrien Béraud's avatar
Adrien Béraud committed
351
        JAMI_DBG("[call:%s] Answering with contact header: %.*s",
352
                 getCallId().c_str(), (int)contactHeader_.slen, contactHeader_.ptr);
353 354 355
        sip_utils::addContactHeader(&contactHeader_, tdata);
    }

356 357
    if (pjsip_inv_send_msg(inv.get(), tdata) != PJ_SUCCESS) {
        inv.reset();
358
        throw std::runtime_error("Could not send invite request answer (200 OK)");
359
    }
360

361
    setState(CallState::ACTIVE, ConnectionState::CONNECTED);
362 363
}

364 365 366
void
SIPCall::hangup(int reason)
{
367
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
368 369 370 371 372 373 374
    if (inv and inv->dlg) {
        pjsip_route_hdr *route = inv->dlg->route_set.next;
        while (route and route != &inv->dlg->route_set) {
            char buf[1024];
            int printed = pjsip_hdr_print_on(route, buf, sizeof(buf));
            if (printed >= 0) {
                buf[printed] = '\0';
Adrien Béraud's avatar
Adrien Béraud committed
375
                JAMI_DBG("[call:%s] Route header %s", getCallId().c_str(), buf);
376 377
            }
            route = route->next;
378
        }
379 380 381 382 383 384
        const int status = reason ? reason :
                           inv->state <= PJSIP_INV_STATE_EARLY and inv->role != PJSIP_ROLE_UAC ?
                           PJSIP_SC_CALL_TSX_DOES_NOT_EXIST :
                           inv->state >= PJSIP_INV_STATE_DISCONNECTED ? PJSIP_SC_DECLINE : 0;
        // Notify the peer
        terminateSipSession(status);
385
    }
386 387
    // Stop all RTP streams
    stopAllMedia();
388
    setState(Call::ConnectionState::DISCONNECTED, reason);
389
    removeCall();
390
}
391 392 393 394

void
SIPCall::refuse()
{
395
    if (!isIncoming() or getConnectionState() == ConnectionState::CONNECTED or !inv)
396 397
        return;

398
    stopAllMedia();
399

400
    // Notify the peer
401
    terminateSipSession(PJSIP_SC_DECLINE);
402

403
    setState(Call::ConnectionState::DISCONNECTED, ECONNABORTED);
404
    removeCall();
405
}
406 407 408 409

static void
transfer_client_cb(pjsip_evsub *sub, pjsip_event *event)
{
410 411
    auto link = getSIPVoIPLink();
    if (not link) {
Adrien Béraud's avatar
Adrien Béraud committed
412
        JAMI_ERR("no more VoIP link");
413 414 415 416
        return;
    }

    auto mod_ua_id = link->getModId();
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468

    switch (pjsip_evsub_get_state(sub)) {
        case PJSIP_EVSUB_STATE_ACCEPTED:
            if (!event)
                return;

            pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
            break;

        case PJSIP_EVSUB_STATE_TERMINATED:
            pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL);
            break;

        case PJSIP_EVSUB_STATE_ACTIVE: {
            if (!event)
                return;

            pjsip_rx_data* r_data = event->body.rx_msg.rdata;

            if (!r_data)
                return;

            std::string request(pjsip_rx_data_get_info(r_data));

            pjsip_status_line status_line = { 500, *pjsip_get_status_text(500) };

            if (!r_data->msg_info.msg)
                return;

            if (r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD and
                    request.find("NOTIFY") != std::string::npos) {
                pjsip_msg_body *body = r_data->msg_info.msg->body;

                if (!body)
                    return;

                if (pj_stricmp2(&body->content_type.type, "message") or
                        pj_stricmp2(&body->content_type.subtype, "sipfrag"))
                    return;

                if (pjsip_parse_status_line((char*) body->data, body->len, &status_line) != PJ_SUCCESS)
                    return;
            }

            if (!r_data->msg_info.cid)
                return;

            auto call = static_cast<SIPCall *>(pjsip_evsub_get_mod_data(sub, mod_ua_id));
            if (!call)
                return;

            if (status_line.code / 100 == 2) {
469 470
                if (call->inv)
                    call->terminateSipSession(PJSIP_SC_GONE);
471 472 473 474 475 476 477
                Manager::instance().hangupCall(call->getCallId());
                pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL);
            }

            break;
        }

478 479 480 481
        case PJSIP_EVSUB_STATE_NULL:
        case PJSIP_EVSUB_STATE_SENT:
        case PJSIP_EVSUB_STATE_PENDING:
        case PJSIP_EVSUB_STATE_UNKNOWN:
482 483 484 485 486 487
        default:
            break;
    }
}

bool
488
SIPCall::transferCommon(const pj_str_t *dst)
489
{
490
    if (not inv or not inv->dlg)
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
        return false;

    pjsip_evsub_user xfer_cb;
    pj_bzero(&xfer_cb, sizeof(xfer_cb));
    xfer_cb.on_evsub_state = &transfer_client_cb;

    pjsip_evsub *sub;

    if (pjsip_xfer_create_uac(inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
        return false;

    /* Associate this voiplink of call with the client subscription
     * We can not just associate call with the client subscription
     * because after this function, we can no find the cooresponding
     * voiplink from the call any more. But the voiplink is useful!
     */
507
    pjsip_evsub_set_mod_data(sub, getSIPVoIPLink()->getModId(), this);
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526

    /*
     * Create REFER request.
     */
    pjsip_tx_data *tdata;

    if (pjsip_xfer_initiate(sub, dst, &tdata) != PJ_SUCCESS)
        return false;

    /* Send. */
    if (pjsip_xfer_send_request(sub, tdata) != PJ_SUCCESS)
        return false;

    return true;
}

void
SIPCall::transfer(const std::string& to)
{
527
    auto& account = getSIPAccount();
528

529 530
    if (Recordable::isRecording()) {
        deinitRecorder();
531
        stopRecording();
532
    }
533

534 535
    std::string toUri = account.getToUri(to);
    const pj_str_t dst(CONST_PJ_STR(toUri));
Adrien Béraud's avatar
Adrien Béraud committed
536
    JAMI_DBG("[call:%s] Transferring to %.*s", getCallId().c_str(), (int)dst.slen, dst.ptr);
537 538 539 540 541 542

    if (!transferCommon(&dst))
        throw VoipLinkException("Couldn't transfer");
}

bool
543
SIPCall::attendedTransfer(const std::string& to)
544
{
545 546 547 548 549
    const auto toCall = Manager::instance().callFactory.getCall<SIPCall>(to);
    if (!toCall)
        return false;

    if (not toCall->inv or not toCall->inv->dlg)
550
        return false;
551

552
    pjsip_dialog *target_dlg = toCall->inv->dlg;
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);

    char str_dest_buf[PJSIP_MAX_URL_SIZE * 2] = { '<' };
    pj_str_t dst = { str_dest_buf, 1 };

    dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf + 1, sizeof(str_dest_buf) - 1);
    dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen,
                                 sizeof(str_dest_buf) - dst.slen,
                                 "?"
                                 "Replaces=%.*s"
                                 "%%3Bto-tag%%3D%.*s"
                                 "%%3Bfrom-tag%%3D%.*s>",
                                 (int)target_dlg->call_id->id.slen,
                                 target_dlg->call_id->id.ptr,
                                 (int)target_dlg->remote.info->tag.slen,
                                 target_dlg->remote.info->tag.ptr,
                                 (int)target_dlg->local.info->tag.slen,
                                 target_dlg->local.info->tag.ptr);

    return transferCommon(&dst);
}
574

575
bool
576 577
SIPCall::onhold()
{
Sébastien Blin's avatar
Sébastien Blin committed
578 579 580 581 582
    // If ICE is currently negotiating, we must wait before hold the call
    if (isWaitingForIceAndMedia_) {
        remainingRequest_ = Request::HoldingOn;
        return false;
    }
583
    if (not setState(CallState::HOLD))
584
        return false;
585

586
    stopAllMedia();
587

588
    if (getConnectionState() == ConnectionState::CONNECTED) {
Sébastien Blin's avatar
Sébastien Blin committed
589
        if (SIPSessionReinvite() != PJ_SUCCESS) {
Adrien Béraud's avatar
Adrien Béraud committed
590
            JAMI_WARN("[call:%s] Reinvite failed", getCallId().c_str());
Sébastien Blin's avatar
Sébastien Blin committed
591 592
            return true;
        }
593
    }
594

Sébastien Blin's avatar
Sébastien Blin committed
595 596
    isWaitingForIceAndMedia_ = true;

597
    return true;
598 599
}

600
bool
601 602
SIPCall::offhold()
{
603
    bool success = false;
Sébastien Blin's avatar
Sébastien Blin committed
604 605 606 607 608 609
    // If ICE is currently negotiating, we must wait before unhold the call
    if (isWaitingForIceAndMedia_) {
        remainingRequest_ = Request::HoldingOff;
        return false;
    }

610
    auto& account = getSIPAccount();
611 612

    try {
613
        if (account.isStunEnabled())
614
            success = internalOffHold([&] { updateSDPFromSTUN(); });
615
        else
616
            success = internalOffHold([] {});
617 618

    } catch (const SdpException &e) {
Adrien Béraud's avatar
Adrien Béraud committed
619
        JAMI_ERR("[call:%s] %s", getCallId().c_str(), e.what());
620
        throw VoipLinkException("SDP issue in offhold");
Tristan Matthews's avatar
Tristan Matthews committed
621
    }
622

Sébastien Blin's avatar
Sébastien Blin committed
623 624
    if (success) isWaitingForIceAndMedia_ = true;

625
    return success;
626 627
}

628
bool
Guillaume Roguez's avatar
Guillaume Roguez committed
629
SIPCall::internalOffHold(const std::function<void()>& sdp_cb)
630
{
631
    if (not setState(CallState::ACTIVE))
632
        return false;
633

Guillaume Roguez's avatar
Guillaume Roguez committed
634
    sdp_cb();
635

636
    if (getConnectionState() == ConnectionState::CONNECTED) {
637
        if (SIPSessionReinvite() != PJ_SUCCESS) {
Adrien Béraud's avatar
Adrien Béraud committed
638
            JAMI_WARN("[call:%s] resuming hold", getCallId().c_str());
639
            onhold();
640
            return false;
641
        }
642
    }
643 644

    return true;
645
}
646

647 648 649
void
SIPCall::switchInput(const std::string& resource)
{
650
    mediaInput_ = resource;
Sébastien Blin's avatar
Sébastien Blin committed
651 652 653 654 655 656 657
    if (isWaitingForIceAndMedia_) {
        remainingRequest_ = Request::SwitchInput;
    } else {
        if (SIPSessionReinvite() == PJ_SUCCESS) {
            isWaitingForIceAndMedia_ = true;
        }
    }
658 659
}

660 661 662
void
SIPCall::peerHungup()
{
663
    // Stop all RTP streams
Guillaume Roguez's avatar
Guillaume Roguez committed
664
    stopAllMedia();
665

666 667 668
    if (inv)
        terminateSipSession(PJSIP_SC_NOT_FOUND);
    else
Adrien Béraud's avatar
Adrien Béraud committed
669
        JAMI_ERR("[call:%s] peerHungup: no invite session for this call", getCallId().c_str());
670

671
    Call::peerHungup();
672
}
673 674 675 676

void
SIPCall::carryingDTMFdigits(char code)
{
677 678 679 680 681 682 683 684 685 686 687 688 689
    int duration = Manager::instance().voipPreferences.getPulseLength();
    char dtmf_body[1000];

    // handle flash code
    if (code == '!') {
        snprintf(dtmf_body, sizeof dtmf_body - 1, "Signal=16\r\nDuration=%d\r\n", duration);
    } else {
        snprintf(dtmf_body, sizeof dtmf_body - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration);
    }

    try {
        sendSIPInfo(dtmf_body, "dtmf-relay");
    } catch (const std::exception& e) {
Adrien Béraud's avatar
Adrien Béraud committed
690
        JAMI_ERR("Error sending DTMF: %s", e.what());
691
    }
692
}
693

Adrien Béraud's avatar
Adrien Béraud committed
694 695 696 697 698 699 700 701 702
void
SIPCall::setVideoOrientation(int rotation)
{
    std::string sip_body =
        "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
        "<media_control><vc_primitive><to_encoder>"
        "<device_orientation=" + std::to_string(rotation) + "/>"
        "</to_encoder></vc_primitive></media_control>";

Adrien Béraud's avatar
Adrien Béraud committed
703
    JAMI_DBG("Sending device orientation via SIP INFO");
Adrien Béraud's avatar
Adrien Béraud committed
704 705 706 707

    sendSIPInfo(sip_body.c_str(), "media_control+xml");
}

708 709
void
SIPCall::sendTextMessage(const std::map<std::string, std::string>& messages,
710
                         const std::string& from)
711
{
712
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
713 714 715
    //TODO: for now we ignore the "from" (the previous implementation for sending this info was
    //      buggy and verbose), another way to send the original message sender will be implemented
    //      in the future
716
    if (not subcalls_.empty()) {
717
        pendingOutMessages_.emplace_back(messages, from);
718
        for (auto& c : subcalls_)
719 720
            c->sendTextMessage(messages, from);
    } else {
721
        if (inv) {
722
            try {
723
                im::sendSipMessage(inv.get(), messages);
724
            } catch (...) {}
725 726
        } else {
            pendingOutMessages_.emplace_back(messages, from);
Adrien Béraud's avatar
Adrien Béraud committed
727
            JAMI_ERR("[call:%s] sendTextMessage: no invite session for this call", getCallId().c_str());
728
        }
729
    }
730
}
731

732 733 734
void
SIPCall::removeCall()
{
735
    std::lock_guard<std::recursive_mutex> lk {callMutex_};
Adrien Béraud's avatar
Adrien Béraud committed
736
    JAMI_WARN("[call:%s] removeCall()", getCallId().c_str());
737
    Call::removeCall();
738
    mediaTransport_.reset();
739 740 741 742
    inv.reset();
    setTransport({});
}

743
void
744
SIPCall::onFailure(signed cause)
745
{
746 747 748 749 750 751 752 753 754
    if (setState(CallState::MERROR, ConnectionState::DISCONNECTED, cause)) {
        runOnMainThread([w = weak()] {
            if (auto shared = w.lock()) {
                auto& call = *shared;
                Manager::instance().callFailure(call);
                call.removeCall();
            }
        });
    }
755 756
}

757 758 759
void
SIPCall::onBusyHere()
{
760 761 762 763 764
    if (getCallType() == CallType::OUTGOING)
        setState(CallState::PEER_BUSY, ConnectionState::DISCONNECTED);
    else
        setState(CallState::BUSY, ConnectionState::DISCONNECTED);

765
    runOnMainThread([w = weak()] {
766 767 768 769 770 771
        if (auto shared = w.lock()) {
            auto& call = *shared;
            Manager::instance().callBusy(call);
            call.removeCall();
        }
    });
772 773
}

774 775 776
void
SIPCall::onClosed()
{
777
    runOnMainThread([w = weak()] {
778 779 780 781
        if (auto shared = w.lock()) {
            auto& call = *shared;
            Manager::instance().peerHungupCall(call);
            call.removeCall();
782
            Manager::instance().checkAudio();
783 784
        }
    });
785 786 787 788 789
}

void
SIPCall::onAnswered()
{
Adrien Béraud's avatar
Adrien Béraud committed
790
    JAMI_WARN("[call:%s] onAnswered()", getCallId().c_str());
791 792 793 794 795
    runOnMainThread([w = weak()] {
        if (auto shared = w.lock()) {
            if (shared->getConnectionState() != ConnectionState::CONNECTED) {
                shared->setState(CallState::ACTIVE, ConnectionState::CONNECTED);
                if (not shared->isSubcall()) {
796 797
                    Manager::instance().peerAnsweredCall(*shared);
                }
798
            }
799
        }
800
    });
801
}
802

803 804 805
void
SIPCall::sendKeyframe()
{
Adrien Béraud's avatar
Adrien Béraud committed
806
#ifdef ENABLE_VIDEO
Adrien Béraud's avatar
Adrien Béraud committed
807
    dht::ThreadPool::computation().run([w = weak()] {
808
        if (auto sthis = w.lock()) {
Adrien Béraud's avatar
Adrien Béraud committed
809
            JAMI_DBG("handling picture fast update request");
810 811 812 813 814 815
            sthis->getVideoRtp().forceKeyFrame();
        }
    });
#endif
}

816 817 818
void
SIPCall::onPeerRinging()
{
819
    setState(ConnectionState::RINGING);
820 821
}

822 823 824
void
SIPCall::setupLocalSDPFromIce()
{
825 826 827
    auto media_tr = getIceMediaTransport();

    if (not media_tr) {
Adrien Béraud's avatar
Adrien Béraud committed
828
        JAMI_WARN("[call:%s] no media ICE transport, SDP not changed", getCallId().c_str());
829 830 831
        return;
    }

832
    // we need an initialized ICE to progress further
833
    if (media_tr->waitForInitialization(DEFAULT_ICE_INIT_TIMEOUT) <= 0) {
Adrien Béraud's avatar
Adrien Béraud committed
834
        JAMI_ERR("[call:%s] Medias' ICE init failed", getCallId().c_str());
835 836 837
        return;
    }

838 839 840 841 842
    if (const auto& ip = getSIPAccount().getPublishedIpAddress()) {
        for (unsigned compId = 1; compId <= media_tr->getComponentCount(); ++compId)
            media_tr->registerPublicIP(compId, ip);
    }

Adrien Béraud's avatar
Adrien Béraud committed
843
    JAMI_WARN("[call:%s] fill SDP with ICE transport %p", getCallId().c_str(), media_tr);
844
    sdp_->addIceAttributes(media_tr->getLocalAttributes());
845 846

    // Add video and audio channels
847 848
    sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, media_tr->getLocalCandidates(ICE_AUDIO_RTP_COMPID));
    sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, media_tr->getLocalCandidates(ICE_AUDIO_RTCP_COMPID));
Adrien Béraud's avatar
Adrien Béraud committed
849
#ifdef ENABLE_VIDEO
850 851
    sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, media_tr->getLocalCandidates(ICE_VIDEO_RTP_COMPID));
    sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, media_tr->getLocalCandidates(ICE_VIDEO_RTCP_COMPID));
852 853 854
#endif
}

855
std::vector<IceCandidate>
856 857
SIPCall::getAllRemoteCandidates()
{
858
    std::vector<IceCandidate> rem_candidates;
859
    auto media_tr = getIceMediaTransport();
860

861 862
    auto addSDPCandidates = [&, this](unsigned sdpMediaId,
                                      std::vector<IceCandidate>& out) {
863 864
        IceCandidate cand;
        for (auto& line : sdp_->getIceCandidates(sdpMediaId)) {
865
            if (media_tr->getCandidateFromSDP(line, cand)) {
Adrien Béraud's avatar
Adrien Béraud committed
866
                JAMI_DBG("[call:%s] add remote ICE candidate: %s", getCallId().c_str(), line.c_str());
867
                out.emplace_back(cand);
868
            }
869 870 871 872
        }
    };

    addSDPCandidates(SDP_AUDIO_MEDIA_ID, rem_candidates);
Adrien Béraud's avatar
Adrien Béraud committed
873
#ifdef ENABLE_VIDEO
874 875 876 877 878 879
    addSDPCandidates(SDP_VIDEO_MEDIA_ID, rem_candidates);
#endif

    return rem_candidates;
}

880 881
std::shared_ptr<AccountCodecInfo>
SIPCall::getVideoCodec() const
882
{
Adrien Béraud's avatar
Adrien Béraud committed
883
#ifdef ENABLE_VIDEO
884
    return videortp_->getCodec();
885
#endif
886 887 888 889 890 891 892
    return {};
}

std::shared_ptr<AccountCodecInfo>
SIPCall::getAudioCodec() const
{
    return avformatrtp_->getCodec();
893 894
}

895
void
896
SIPCall::startAllMedia()
897
{
Adrien Béraud's avatar
Adrien Béraud committed
898
    JAMI_WARN("[call:%s] startAllMedia()", getCallId().c_str());
899
    if (isSecure() && not transport_->isSecure()) {
Adrien Béraud's avatar
Adrien Béraud committed
900
        JAMI_ERR("[call:%s] Can't perform secure call over insecure SIP transport",
901
                 getCallId().c_str());
902
        onFailure(EPROTONOSUPPORT);
903 904
        return;
    }
Guillaume Roguez's avatar
Guillaume Roguez committed
905 906
    auto slots = sdp_->getMediaSlots();
    unsigned ice_comp_id = 0;
907
    bool peer_holding {true};
908
    int slotN = -1;
909

Adrien Béraud's avatar
Adrien Béraud committed
910
#ifdef ENABLE_VIDEO
911 912 913 914 915 916
    videortp_->setRequestKeyFrameCallback([wthis = weak()] {
        runOnMainThread([wthis] {
            if (auto this_ = wthis.lock())
                this_->requestKeyframe();
        });
    });
Adrien Béraud's avatar
Adrien Béraud committed
917 918 919 920 921 922
    videortp_->setChangeOrientationCallback([wthis = weak()] (int angle) {
        runOnMainThread([wthis, angle] {
            if (auto this_ = wthis.lock())
                this_->setVideoOrientation(angle);
        });
    });
923 924
#endif

Guillaume Roguez's avatar
Guillaume Roguez committed
925
    for (const auto& slot : slots) {
926
        ++slotN;
Guillaume Roguez's avatar
Guillaume Roguez committed
927 928
        const auto& local = slot.first;
        const auto& remote = slot.second;
929

Guillaume Roguez's avatar
Guillaume Roguez committed
930
        if (local.type != remote.type) {
Adrien Béraud's avatar
Adrien Béraud committed
931
            JAMI_ERR("[call:%s] [SDP:slot#%u] Inconsistent media types between local and remote",
932
                     getCallId().c_str(), slotN);
Guillaume Roguez's avatar
Guillaume Roguez committed
933
            continue;
934
        }
935

936 937
        RtpSession* rtp = local.type == MEDIA_AUDIO
            ? static_cast<RtpSession*>(avformatrtp_.get())
Adrien Béraud's avatar
Adrien Béraud committed
938
#ifdef ENABLE_VIDEO
939
            : static_cast<RtpSession*>(videortp_.get());
940 941 942 943 944 945 946
#else
            : nullptr;
#endif

        if (not rtp)
            continue;

947
        if (!local.codec) {
Adrien Béraud's avatar
Adrien Béraud committed
948
            JAMI_WARN("[call:%s] [SDP:slot#%u] Missing local codec", getCallId().c_str(),
949
                      slotN);
950 951 952
            continue;
        }
        if (!remote.codec) {
Adrien Béraud's avatar
Adrien Béraud committed
953
            JAMI_WARN("[call:%s] [SDP:slot#%u] Missing remote codec", getCallId().c_str(),
954
                      slotN);
955 956 957
            continue;
        }

958 959
        peer_holding &= remote.holding;

960
        if (isSecure() && (not local.crypto || not remote.crypto)) {
Adrien Béraud's avatar
Adrien Béraud committed
961
            JAMI_ERR("[call:%s] [SDP:slot#%u] Can't perform secure call over insecure RTP transport",
962
                     getCallId().c_str(), slotN);
963 964
            continue;
        }
965

966
        auto new_mtu = transport_->getTlsMtu();
967 968
        if (local.type & MEDIA_AUDIO)
            avformatrtp_->switchInput(mediaInput_);
969 970
        avformatrtp_->setMtu(new_mtu);

Adrien Béraud's avatar
Adrien Béraud committed
971
#ifdef ENABLE_VIDEO
972 973
        if (local.type & MEDIA_VIDEO)
            videortp_->switchInput(mediaInput_);
974 975
        videortp_->setMtu(new_mtu);
#endif
976
        rtp->updateMedia(remote, local);
977

978 979
        rtp->setSuccessfulSetupCb([this](MediaType type){ rtpSetupSuccess(type); });

980 981 982 983 984 985 986 987
        // Not restarting media loop on hold as it's a huge waste of CPU ressources
        // because of the audio loop
        if (getState() != CallState::HOLD) {
            if (isIceRunning()) {
                rtp->start(newIceSocket(ice_comp_id + 0),
                           newIceSocket(ice_comp_id + 1));
                ice_comp_id += 2;
            } else
988
                rtp->start(nullptr, nullptr);
989
        }
990 991

        switch (local.type) {
Adrien Béraud's avatar
Adrien Béraud committed
992
#ifdef ENABLE_VIDEO
993
            case MEDIA_VIDEO:
994
                isVideoMuted_ = mediaInput_.empty();
995 996 997 998 999 1000 1001
                break;
#endif
            case MEDIA_AUDIO:
                isAudioMuted_ = not rtp->isSending();
                break;
            default: break;
        }
1002
    }
1003

1004
    if (not isSubcall() and peerHolding_ != peer_holding) {
1005 1006 1007
        peerHolding_ = peer_holding;
        emitSignal<DRing::CallSignal::PeerHold>(getCallId(), peerHolding_);
    }
Sébastien Blin's avatar
Sébastien Blin committed
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026

    // Media is restarted, we can process the last holding request.
    isWaitingForIceAndMedia_ = false;
    if (remainingRequest_ != Request::NoRequest) {
        switch (remainingRequest_) {
        case Request::HoldingOn:
            onhold();
            break;
        case Request::HoldingOff:
            offhold();
            break;
        case Request::SwitchInput:
            SIPSessionReinvite();
            break;
        default:
            break;
        }
        remainingRequest_ = Request::NoRequest;
    }
1027
}
1028

1029 1030 1031
void
SIPCall::restartMediaSender()
{
Adrien Béraud's avatar
Adrien Béraud committed
1032
    JAMI_DBG("[call:%s] restarting TX media streams", getCallId().c_str());
1033
    avformatrtp_->restartSender();
Adrien Béraud's avatar
Adrien Béraud committed
1034
#ifdef ENABLE_VIDEO
1035
    videortp_->restartSender();
1036 1037 1038
#endif
}

1039
void
Guillaume Roguez's avatar
Guillaume Roguez committed
1040
SIPCall::stopAllMedia()
1041
{
Adrien Béraud's avatar
Adrien Béraud committed
1042
    JAMI_DBG("[call:%s] stopping all medias", getCallId().c_str());
1043 1044 1045 1046
    if (Recordable::isRecording()) {
        deinitRecorder();
        stopRecording(); // if call stops, finish recording
    }
1047
    avformatrtp_->stop();
Adrien Béraud's avatar
Adrien Béraud committed
1048
#ifdef ENABLE_VIDEO
1049
    videortp_->stop();
1050 1051
#endif
}
1052

1053
void
1054
SIPCall::muteMedia(const std::string& mediaType, bool mute)
1055 1056
{
    if (mediaType.compare(DRing::Media::Details::MEDIA_TYPE_VIDEO) == 0) {
Adrien Béraud's avatar
Adrien Béraud committed
1057
#ifdef ENABLE_VIDEO
1058
        if (mute == isVideoMuted_) return;
Adrien Béraud's avatar
Adrien Béraud committed
1059
        JAMI_WARN("[call:%s] video muting %s", getCallId().c_str(), bool_to_str(mute));
1060
        isVideoMuted_ = mute;
1061 1062
        mediaInput_ = isVideoMuted_ ? "" : Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice();
        DRing::switchInput(getCallId(), mediaInput_);
1063
        if (not isSubcall())
1064
            emitSignal<DRing::CallSignal::VideoMuted>(getCallId(), isVideoMuted_);
1065 1066
#endif
    } else if (mediaType.compare(DRing::Media::Details::MEDIA_TYPE_AUDIO) == 0) {
1067
        if (mute == isAudioMuted_) return;
Adrien Béraud's avatar
Adrien Béraud committed
1068
        JAMI_WARN("[call:%s] audio muting %s", getCallId().c_str(), bool_to_str(mute));
1069
        isAudioMuted_ = mute;
1070
        avformatrtp_->setMuted(isAudioMuted_);
1071
        if (not isSubcall())
1072
            emitSignal<DRing::CallSignal::AudioMuted>(getCallId(), isAudioMuted_);
1073 1074 1075
    }
}

1076 1077 1078 1079 1080 1081
/// \brief Prepare media transport and launch media stream based on negotiated SDP
///
/// This method has to be called by link (ie SipVoIpLink) when SDP is negotiated and
/// media streams structures are knows.
/// In case of ICE transport used, the medias streams are launched asynchonously when
/// the transport is negotiated.
Guillaume Roguez's avatar
Guillaume Roguez committed
1082 1083 1084
void
SIPCall::onMediaUpdate()
{
Adrien Béraud's avatar
Adrien Béraud committed
1085
    JAMI_WARN("[call:%s] medias changed", getCallId().c_str());
1086 1087 1088 1089

    // If ICE is not used, start medias now
    auto rem_ice_attrs = sdp_->getIceAttributes();
    if (rem_ice_attrs.ufrag.empty() or rem_ice_attrs.pwd.empty()) {
Adrien Béraud's avatar
Adrien Béraud committed
1090
        JAMI_WARN("[call:%s] no remote ICE for medias", getCallId().c_str());
1091
        stopAllMedia();
1092
        startAllMedia();
1093
        return;
1094
    }
1095

1096 1097
    // Main call (no subcalls) must wait for ICE now, the rest of code needs to access
    // to a negotiated transport.
1098 1099 1100 1101 1102
    runOnMainThread([w = weak()] {
        if (auto this_ = w.lock())
            if (not this_->isSubcall())
                this_->waitForIceAndStartMedia();
    });
1103 1104 1105 1106 1107
}

void
SIPCall::waitForIceAndStartMedia()
{
1108
    // Initialization waiting task
1109
    Manager::instance().addTask([weak_call = weak()] {
1110 1111 1112 1113
        // TODO: polling algo, to it by event
        if (auto call = weak_call.lock()) {
            auto ice = call->getIceMediaTransport();

1114
            if (not ice or ice->isFailed()) {
Adrien Béraud's avatar
Adrien Béraud committed
1115
                JAMI_ERR("[call:%s] Media ICE init failed", call->getCallId().c_str());
1116
                call->onFailure(EIO);
1117 1118
                return false;
            }
1119 1120 1121 1122

            if (!ice->isInitialized())
                return true;

Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
1123
            // Start transport on SDP data and wait for negotiation
1124 1125
            auto rem_ice_attrs = call->sdp_->getIceAttributes();
            if (rem_ice_attrs.ufrag.empty() or rem_ice_attrs.pwd.empty()) {
Adrien Béraud's avatar
Adrien Béraud committed
1126
                JAMI_ERR("[call:%s] Media ICE attributes empty", call->getCallId().c_str());
1127
                call->onFailure(EIO);
1128 1129
                return false;
            }