call.cpp 10 KB
Newer Older
yanmorin's avatar
yanmorin committed
1
/*
2 3
 *  Copyright (C) 2004-2015 Savoir-faire Linux Inc.
 *
jpbl's avatar
jpbl committed
4
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
5 6
 *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
 *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
yanmorin's avatar
yanmorin committed
7
 *
jpbl's avatar
jpbl committed
8 9
 *  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
10
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
11
 *  (at your option) any later version.
yanmorin's avatar
yanmorin committed
12
 *
jpbl's avatar
jpbl committed
13 14 15 16
 *  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.
yanmorin's avatar
yanmorin committed
17
 *
jpbl's avatar
jpbl committed
18 19
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
jpbl's avatar
jpbl committed
21
 */
22

jpbl's avatar
jpbl committed
23
#include "call.h"
24
#include "account.h"
25
#include "manager.h"
26
#include "audio/ringbufferpool.h"
27
#include "dring/call_const.h"
28
#include "client/ring_signal.h"
29
#include "audio/audiorecord.h"
30 31
#include "sip/sip_utils.h"
#include "ip_utils.h"
32
#include "array_size.h"
33 34
#include "map_utils.h"
#include "call_factory.h"
35
#include "string_utils.h"
36
#include "enumclass_utils.h"
37

38 39
#include "errno.h"

40 41
namespace ring {

42
Call::Call(Account& account, const std::string& id, Call::CallType type)
Adrien Béraud's avatar
Adrien Béraud committed
43
    : id_(id)
44
    , type_(type)
45
    , account_(account)
46 47
{
    time(&timestamp_start_);
48
    account_.attachCall(id_);
49
}
jpbl's avatar
jpbl committed
50

Julien Bonjean's avatar
Julien Bonjean committed
51
Call::~Call()
52 53 54
{
    account_.detachCall(id_);
}
jpbl's avatar
jpbl committed
55

56 57 58
void
Call::removeCall()
{
59
    auto this_ = shared_from_this();
60
    Manager::instance().callFactory.removeCall(*this);
61
    iceTransport_.reset();
62
    setState(CallState::OVER);
63 64 65 66
}

const std::string&
Call::getAccountId() const
67 68 69 70
{
    return account_.getAccountID();
}

yanmorin's avatar
yanmorin committed
71
Call::ConnectionState
72
Call::getConnectionState() const
Julien Bonjean's avatar
Julien Bonjean committed
73
{
74
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
75
    return connectionState_;
jpbl's avatar
jpbl committed
76 77
}

78 79 80 81 82 83 84
Call::CallState
Call::getState() const
{
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    return callState_;
}

85
bool
86
Call::validStateTransition(CallState newState)
87
{
88 89 90
    // Notice to developper:
    // - list only permitted transition (return true)
    // - let non permitted ones as default case (return false)
91 92 93 94 95

    // always permited
    if (newState == CallState::OVER)
        return true;

96
    switch (callState_) {
97
        case CallState::INACTIVE:
98
            switch (newState) {
99 100 101
                case CallState::ACTIVE:
                case CallState::BUSY:
                case CallState::MERROR:
102
                    return true;
103 104
                default: // INACTIVE, HOLD
                    return false;
105 106
            }

107
        case CallState::ACTIVE:
108
            switch (newState) {
109
                case CallState::HOLD:
110
                case CallState::MERROR:
111
                    return true;
112
                default: // INACTIVE, ACTIVE, BUSY
113 114 115
                    return false;
            }

116
        case CallState::HOLD:
117
            switch (newState) {
118
                case CallState::ACTIVE:
119 120 121 122 123 124 125 126 127
                case CallState::MERROR:
                    return true;
                default: // INACTIVE, HOLD, BUSY, MERROR
                    return false;
            }

        case CallState::BUSY:
            switch (newState) {
                case CallState::MERROR:
128
                    return true;
129
                default: // INACTIVE, ACTIVE, HOLD, BUSY
130 131 132
                    return false;
            }

133
        default: // MERROR
134 135 136 137 138
            return false;
    }
}

bool
139
Call::setState(CallState call_state, ConnectionState cnx_state, signed code)
Julien Bonjean's avatar
Julien Bonjean committed
140
{
141 142
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    RING_DBG("[call:%s] state change %u/%u, cnx %u/%u, code %d", id_.c_str(),
143 144
             (unsigned)callState_, (unsigned)call_state, (unsigned)connectionState_,
             (unsigned)cnx_state, code);
145 146 147 148

    if (callState_ != call_state) {
        if (not validStateTransition(call_state)) {
            RING_ERR("[call:%s] invalid call state transition from %u to %u",
149
                     id_.c_str(), (unsigned)callState_, (unsigned)call_state);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
            return false;
        }
    } else if (connectionState_ == cnx_state)
        return true; // no changes as no-op

    // Emit client state only if changed
    auto old_client_state = getStateStr();
    callState_ = call_state;
    connectionState_ = cnx_state;
    auto new_client_state = getStateStr();

    if (old_client_state != new_client_state) {
        RING_DBG("[call:%s] emit client call state change %s, code %d",
                 id_.c_str(), new_client_state.c_str(), code);
        emitSignal<DRing::CallSignal::StateChange>(id_, new_client_state, code);
165 166 167
    }

    return true;
jpbl's avatar
jpbl committed
168 169
}

170 171
bool
Call::setState(CallState call_state, signed code)
Julien Bonjean's avatar
Julien Bonjean committed
172
{
173 174 175 176 177 178 179 180 181
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    return setState(call_state, connectionState_, code);
}

bool
Call::setState(ConnectionState cnx_state, signed code)
{
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
    return setState(callState_, cnx_state, code);
jpbl's avatar
jpbl committed
182 183
}

184
std::string
185
Call::getStateStr() const
Julien Bonjean's avatar
Julien Bonjean committed
186
{
187 188
    using namespace DRing::Call;

189
    switch (getState()) {
190
        case CallState::ACTIVE:
191
            switch (getConnectionState()) {
192 193 194
                case ConnectionState::PROGRESSING:
                    return StateEvent::CONNECTING;

195
                case ConnectionState::RINGING:
196 197 198 199 200
                    return isIncoming() ? StateEvent::INCOMING : StateEvent::RINGING;

                case ConnectionState::DISCONNECTED:
                    return StateEvent::HUNGUP;

201
                case ConnectionState::CONNECTED:
202
                default:
203
                    return StateEvent::CURRENT;
204 205
            }

206
        case CallState::HOLD:
207 208
            if(getConnectionState() == ConnectionState::DISCONNECTED)
                return StateEvent::HUNGUP;
209 210
            return StateEvent::HOLD;

211
        case CallState::BUSY:
212
            return StateEvent::BUSY;
213

214
        case CallState::INACTIVE:
215
            switch (getConnectionState()) {
216 217 218
                case ConnectionState::PROGRESSING:
                    return StateEvent::CONNECTING;

219
                case ConnectionState::RINGING:
220 221
                    return isIncoming() ? StateEvent::INCOMING : StateEvent::RINGING;

222
                case ConnectionState::CONNECTED:
223 224
                    return StateEvent::CURRENT;

225
                default:
226
                    return StateEvent::INACTIVE;
227 228
            }

229 230 231
        case CallState::OVER:
            return StateEvent::OVER;

232
        case CallState::MERROR:
233
        default:
234
            return StateEvent::FAILURE;
235 236 237
    }
}

Adrien Béraud's avatar
Adrien Béraud committed
238
IpAddr
239
Call::getLocalIp() const
Julien Bonjean's avatar
Julien Bonjean committed
240
{
241
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
Adrien Béraud's avatar
Adrien Béraud committed
242
    return localAddr_;
243 244
}

245
unsigned int
246
Call::getLocalAudioPort() const
Julien Bonjean's avatar
Julien Bonjean committed
247
{
248
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
249
    return localAudioPort_;
250 251
}

252
unsigned int
253
Call::getLocalVideoPort() const
254
{
255
    std::lock_guard<std::recursive_mutex> lock(callMutex_);
256
    return localVideoPort_;
257 258
}

259
bool
260
Call::toggleRecording()
Julien Bonjean's avatar
Julien Bonjean committed
261
{
262
    const bool startRecording = Recordable::toggleRecording();
263
    std::string process_id = Recordable::recAudio_->getRecorderID();
264
    RingBufferPool &rbPool = Manager::instance().getRingBufferPool();
265

266
    if (startRecording) {
267
        rbPool.bindHalfDuplexOut(process_id, id_);
268
        rbPool.bindHalfDuplexOut(process_id, RingBufferPool::DEFAULT_ID);
269
    } else {
270
        rbPool.unBindHalfDuplexOut(process_id, id_);
271
        rbPool.unBindHalfDuplexOut(process_id, RingBufferPool::DEFAULT_ID);
272 273
    }

274
    return startRecording;
275
}
276

277
std::map<std::string, std::string>
278
Call::getDetails() const
279
{
280
    return {
281
        {DRing::Call::Details::CALL_TYPE,        ring::to_string((unsigned)type_)},
282
        {DRing::Call::Details::PEER_NUMBER,      peerNumber_},
283
        {DRing::Call::Details::DISPLAY_NAME,     peerDisplayName_},
284 285 286 287
        {DRing::Call::Details::CALL_STATE,       getStateStr()},
        {DRing::Call::Details::CONF_ID,          confID_},
        {DRing::Call::Details::TIMESTAMP_START,  ring::to_string(timestamp_start_)},
        {DRing::Call::Details::ACCOUNTID,        getAccountId()},
288 289
        {DRing::Call::Details::AUDIO_MUTED,      std::string(bool_to_str(isAudioMuted_))},
        {DRing::Call::Details::VIDEO_MUTED,      std::string(bool_to_str(isVideoMuted_))},
290
    };
291 292 293 294 295
}

std::map<std::string, std::string>
Call::getNullDetails()
{
296 297 298
    return {
        {DRing::Call::Details::CALL_TYPE,        "0"},
        {DRing::Call::Details::PEER_NUMBER,      ""},
299
        {DRing::Call::Details::DISPLAY_NAME,     ""},
300 301 302 303
        {DRing::Call::Details::CALL_STATE,       "UNKNOWN"},
        {DRing::Call::Details::CONF_ID,          ""},
        {DRing::Call::Details::TIMESTAMP_START,  ""},
        {DRing::Call::Details::ACCOUNTID,        ""},
304
        {DRing::Call::Details::VIDEO_SOURCE,     "UNKNOWN"},
305
    };
306
}
307

308
bool
309 310 311
Call::initIceTransport(bool master, unsigned channel_num)
{
    auto& iceTransportFactory = Manager::instance().getIceTransportFactory();
312 313 314
    iceTransport_ = iceTransportFactory.createTransport(getCallId().c_str(),
                                                        channel_num, master,
                                                        account_.getIceOptions());
315
    return static_cast<bool>(iceTransport_);
316 317 318 319 320
}

int
Call::waitForIceInitialization(unsigned timeout)
{
321
    return iceTransport_->waitForInitialization(timeout);
322 323 324 325 326
}

int
Call::waitForIceNegotiation(unsigned timeout)
{
327
    return iceTransport_->waitForNegotiation(timeout);
328 329 330 331 332
}

bool
Call::isIceUsed() const
{
333
    return iceTransport_ and iceTransport_->isInitialized();
334 335 336 337 338
}

bool
Call::isIceRunning() const
{
339
    return iceTransport_ and iceTransport_->isRunning();
340 341
}

342 343
std::unique_ptr<IceSocket>
Call::newIceSocket(unsigned compId)
344
{
345
    return std::unique_ptr<IceSocket> {new IceSocket(iceTransport_, compId)};
346
}
347

348 349 350 351 352 353 354 355 356
void
Call::peerHungup()
{
    const auto state = getState();
    const auto aborted = state == CallState::ACTIVE or state == CallState::HOLD;
    setState(ConnectionState::DISCONNECTED,
             aborted ? ECONNABORTED : ECONNREFUSED);
}

357
} // namespace ring