LCOV - code coverage report
Current view: top level - src - call.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 82.4 % 34 28
Test Date: 2026-06-13 09:18:46 Functions: 77.3 % 22 17

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  * This program is free software: you can redistribute it and/or modify
       5              :  * it under the terms of the GNU General Public License as published by
       6              :  * the Free Software Foundation, either version 3 of the License, or
       7              :  * (at your option) any later version.
       8              :  *
       9              :  * This program is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  * GNU General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU General Public License
      15              :  * along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : 
      18              : #pragma once
      19              : 
      20              : #ifdef HAVE_CONFIG_H
      21              : #include "config.h"
      22              : #endif
      23              : 
      24              : #include "conference.h"
      25              : #include "media/recordable.h"
      26              : #include "media/peerrecorder.h"
      27              : #include "media/media_codec.h"
      28              : #include "media/media_attribute.h"
      29              : 
      30              : #include <dhtnet/ip_utils.h>
      31              : #include <asio/steady_timer.hpp>
      32              : 
      33              : #include <mutex>
      34              : #include <map>
      35              : #include <memory>
      36              : #include <vector>
      37              : #include <set>
      38              : #include <list>
      39              : #include <functional>
      40              : 
      41              : template<typename T>
      42              : bool
      43          809 : is_uninitialized(std::weak_ptr<T> const& weak)
      44              : {
      45              :     using wt = std::weak_ptr<T>;
      46          809 :     return !weak.owner_before(wt {}) && !wt {}.owner_before(weak);
      47              : }
      48              : 
      49              : namespace jami {
      50              : 
      51              : class VoIPLink;
      52              : class Account;
      53              : class AudioDeviceGuard;
      54              : 
      55              : class Call;
      56              : class Conference;
      57              : 
      58              : using CallMap = std::map<std::string, std::shared_ptr<Call>>;
      59              : 
      60              : namespace video {
      61              : class VideoGenerator;
      62              : }
      63              : 
      64              : /*
      65              :  * @file call.h
      66              :  * @brief A call is the base class for protocol-based calls
      67              :  */
      68              : 
      69              : class Call : public Recordable, public PeerRecorder, public std::enable_shared_from_this<Call>
      70              : {
      71              : public:
      72              :     /**
      73              :      * Tell where we're at with the call. The call gets Connected when we know
      74              :      * from the other end what happened with out call. A call can be 'Connected'
      75              :      * even if the call state is Busy, or Error.
      76              :      *
      77              :      * Audio should be transmitted when ConnectionState = Connected AND
      78              :      * CallState = Active.
      79              :      *
      80              :      * \note modify validStateTransition/getStateStr if this enum changes
      81              :      */
      82              :     enum class ConnectionState : uint8_t { DISCONNECTED, TRYING, PROGRESSING, RINGING, CONNECTED, COUNT__ };
      83              : 
      84              :     /**
      85              :      * The Call State.
      86              :      *
      87              :      * \note modify validStateTransition/getStateStr if this enum changes
      88              :      */
      89              :     enum class CallState : uint8_t { INACTIVE, ACTIVE, HOLD, BUSY, PEER_BUSY, MERROR, OVER, COUNT__ };
      90              : 
      91              :     enum class LinkType : uint8_t { GENERIC, SIP };
      92              : 
      93              :     using SubcallSet = std::set<std::shared_ptr<Call>, std::owner_less<std::shared_ptr<Call>>>;
      94              :     using OnReadyCb = std::function<void(bool)>;
      95              :     using StateListenerCb = std::function<bool(CallState, ConnectionState, int)>;
      96              : 
      97              :     /**
      98              :      * This determines if the call originated from the local user (OUTGOING)
      99              :      * or from some remote peer (INCOMING, MISSED).
     100              :      */
     101              :     enum class CallType : uint8_t { INCOMING, OUTGOING, MISSED };
     102              : 
     103              :     virtual ~Call();
     104              : 
     105            0 :     virtual LinkType getLinkType() const { return LinkType::GENERIC; }
     106              : 
     107              :     /**
     108              :      * Return a reference on the call id
     109              :      * @return call id
     110              :      */
     111        23509 :     const std::string& getCallId() const { return id_; }
     112              : 
     113              :     /**
     114              :      * Return a reference on the conference id
     115              :      * @return call id
     116              :      */
     117          429 :     std::shared_ptr<Conference> getConference() const { return conf_.lock(); }
     118          809 :     bool isConferenceParticipant() const { return not is_uninitialized(conf_); }
     119              : 
     120         3170 :     std::weak_ptr<Account> getAccount() const { return account_; }
     121              :     std::string getAccountId() const;
     122              : 
     123          176 :     CallType getCallType() const { return type_; }
     124              : 
     125              :     /**
     126              :      * Set the peer number (destination on outgoing)
     127              :      * not protected by mutex (when created)
     128              :      * @param number peer number
     129              :      */
     130          390 :     void setPeerNumber(const std::string& number) { peerNumber_ = number; }
     131              : 
     132              :     /**
     133              :      * Get the peer number (destination on outgoing)
     134              :      * not protected by mutex (when created)
     135              :      * @return std::string The peer number
     136              :      */
     137         2445 :     const std::string& getPeerNumber() const { return peerNumber_; }
     138              :     /**
     139              :      * Set the display name (caller in ingoing)
     140              :      * not protected by mutex (when created)
     141              :      * @return std::string The peer display name
     142              :      */
     143          101 :     void setPeerDisplayName(const std::string& name) { peerDisplayName_ = name; }
     144              : 
     145              :     /**
     146              :      * Get "To" from the invite
     147              :      * @note Used to make the difference between incoming calls for accounts and for conversations
     148              :      * @return the "To" that was present in the invite
     149              :      */
     150          631 :     const std::string& toUsername() const { return toUsername_; }
     151              :     /**
     152              :      * Updated by sipvoiplink, corresponds to the "To" in the invite
     153              :      * @param username      "To"
     154              :      */
     155          101 :     void toUsername(const std::string& username) { toUsername_ = username; }
     156              : 
     157              :     /**
     158              :      * Get the peer display name (caller in ingoing)
     159              :      * not protected by mutex (when created)
     160              :      * @return std::string The peer name
     161              :      */
     162              :     const std::string& getPeerDisplayName() const { return peerDisplayName_; }
     163              :     /**
     164              :      * Tell if the call is incoming
     165              :      * @return true if yes false otherwise
     166              :      */
     167         1397 :     bool isIncoming() const { return type_ == CallType::INCOMING; }
     168              : 
     169              :     /**
     170              :      * Set the state of the call (protected by mutex)
     171              :      * @param call_state The call state
     172              :      * @param code Optional SIP response code (see RFC3261)
     173              :      * @return true if the requested state change was valid, false otherwise
     174              :      */
     175              :     bool setState(CallState call_state, signed code = 0);
     176              : 
     177              :     /**
     178              :      * Set the state of the call (protected by mutex)
     179              :      * @param call_state The call state
     180              :      * @param cnx_state The call connection state
     181              :      * @param code Optional SIP response code (see RFC3261)
     182              :      * @return true if the requested state change was valid, false otherwise
     183              :      */
     184              :     bool setState(CallState call_state, ConnectionState cnx_state, signed code = 0);
     185              : 
     186              :     /**
     187              :      * Set the state of the call (protected by mutex)
     188              :      * @param cnx_state The call connection state
     189              :      * @param code Optional SIP response code (see RFC3261)
     190              :      * @return true if the requested state change was valid, false otherwise
     191              :      */
     192              :     bool setState(ConnectionState cnx_state, signed code = 0);
     193              : 
     194              :     /**
     195              :      * Get the call state of the call (protected by mutex)
     196              :      * @return CallState  The call state
     197              :      */
     198              :     CallState getState() const;
     199              : 
     200              :     /**
     201              :      * Get the connection state of the call (protected by mutex)
     202              :      * @return ConnectionState The connection state
     203              :      */
     204              :     ConnectionState getConnectionState() const;
     205              : 
     206              :     std::string getStateStr() const;
     207              : 
     208              :     void setIPToIP(bool IPToIP) { isIPToIP_ = IPToIP; }
     209              : 
     210              :     virtual std::map<std::string, std::string> getDetails() const;
     211              : 
     212              :     /**
     213              :      * Answer a call with a list of media attributes.
     214              :      * @param mediaList The list of the media attributes.
     215              :      * The media attributes set by the caller of this method will
     216              :      * determine the response sent to the peer and the configuration
     217              :      * of the local media.
     218              :      * If the media list is empty, the current media set when the call
     219              :      * was created will be used.
     220              :      */
     221              :     virtual void answer(const std::vector<libjami::MediaMap>& mediaList) = 0;
     222              : 
     223              :     /**
     224              :      * Check the media of an incoming media change request.
     225              :      * This method checks the new media against the current media. It
     226              :      * determines if the differences are significant enough to require
     227              :      * more processing.
     228              :      * For instance, this can be used to check if the a change request
     229              :      * must be reported to the client for confirmation or can be handled
     230              :      * by the daemon.
     231              :      * The conditions that cause this method to return true are implementation
     232              :      * specific.
     233              :      *
     234              :      * @param the new media list from the remote
     235              :      * @return true if the new media differs from the current media
     236              :      **/
     237              :     virtual bool checkMediaChangeRequest(const std::vector<libjami::MediaMap>& remoteMediaList) = 0;
     238              : 
     239              :     /**
     240              :      * Process incoming media change request.
     241              :      *
     242              :      * @param the new media list from the remote
     243              :      */
     244              :     virtual void handleMediaChangeRequest(const std::vector<libjami::MediaMap>& remoteMediaList) = 0;
     245              : 
     246              :     /**
     247              :      * Answer to a media update request.
     248              :      * The media attributes set by the caller of this method will
     249              :      * determine the response to send to the peer and the configuration
     250              :      * of the local media.
     251              :      * @param mediaList The list of media attributes. An empty media
     252              :      * list means the media update request was not accepted, meaning the
     253              :      * call continue with the current media. It's up to the implementation
     254              :      * to determine wether an answer will be sent to the peer.
     255              :      * @param isRemote      True if the media list is from the remote peer
     256              :      */
     257              :     virtual void answerMediaChangeRequest(const std::vector<libjami::MediaMap>& mediaList, bool isRemote = false) = 0;
     258              :     /**
     259              :      * Hang up the call
     260              :      * @param reason
     261              :      */
     262              :     virtual void hangup(int reason) = 0;
     263              : 
     264              :     /**
     265              :      * Refuse incoming call
     266              :      */
     267              :     virtual void refuse() = 0;
     268              : 
     269              :     /**
     270              :      * Transfer a call to specified URI
     271              :      * @param to The recipient of the call
     272              :      */
     273              :     virtual void transfer(const std::string& to) = 0;
     274              : 
     275              :     /**
     276              :      * Attended transfer
     277              :      * @param The target call id
     278              :      * @return True on success
     279              :      */
     280              :     virtual bool attendedTransfer(const std::string& to) = 0;
     281              : 
     282              :     /**
     283              :      * Hold call
     284              :      * @param cb    On hold can be queued if waiting for ICE. This callback will be called when ready
     285              :      * @return bool True on success, False if failed or pending
     286              :      */
     287              :     virtual bool hold(OnReadyCb&& cb) = 0;
     288              : 
     289              :     /**
     290              :      * Resume call from hold state
     291              :      * @param cb    On hold can be queued if waiting for ICE. This callback will be called when ready
     292              :      * @return bool True on success, False if failed or pending
     293              :      */
     294              :     virtual bool resume(OnReadyCb&& cb) = 0;
     295              : 
     296              :     virtual void sendKeyframe(int streamIdx = -1) = 0;
     297              : 
     298              :     /**
     299              :      * Check wether ICE is enabled for media
     300              :      */
     301              :     virtual bool isIceEnabled() const = 0;
     302              : 
     303              :     /**
     304              :      * Peer has hung up a call
     305              :      */
     306              :     virtual void peerHungup();
     307              : 
     308              :     /**
     309              :      * @param code Optional SIP response code (see RFC3261)
     310              :      */
     311              :     virtual void removeCall(int code = 0);
     312              : 
     313              :     /**
     314              :      * Update recording state. Typically used to send notifications
     315              :      * to peers about the local recording session state
     316              :      */
     317              :     virtual void updateRecState(bool state) = 0;
     318              : 
     319          631 :     void addStateListener(StateListenerCb&& listener)
     320              :     {
     321          631 :         std::lock_guard lk {callMutex_};
     322          631 :         stateChangedListeners_.emplace_back(std::move(listener));
     323          631 :     }
     324              : 
     325              :     /**
     326              :      * Attach subcall to this instance.
     327              :      * If this subcall is answered, this subcall and this instance will be merged using merge().
     328              :      */
     329              :     void addSubCall(Call& call);
     330              : 
     331              :     ///
     332              :     /// Return true if this call instance is a subcall (internal call for multi-device handling)
     333              :     ///
     334         3968 :     bool isSubcall() const
     335              :     {
     336         3968 :         std::lock_guard lk {callMutex_};
     337         7936 :         return parent_ != nullptr;
     338         3968 :     }
     339              : 
     340              :     /**
     341              :      * @return Call duration in milliseconds
     342              :      */
     343          277 :     std::chrono::milliseconds getCallDuration() const
     344              :     {
     345          277 :         return duration_start_ == time_point::min()
     346          277 :                    ? std::chrono::milliseconds::zero()
     347          277 :                    : std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - duration_start_);
     348              :     }
     349              : 
     350              :     // media management
     351              :     virtual bool toggleRecording();
     352              : 
     353              :     virtual std::vector<MediaAttribute> getMediaAttributeList() const = 0;
     354              : 
     355              :     virtual std::map<std::string, bool> getAudioStreams() const = 0;
     356              : 
     357              :     /**
     358              :      * Return a map of audio stream IDs to their remote (peer-side) muted state.
     359              :      * Unlike getAudioStreams() which returns the local send muted state,
     360              :      * this returns whether the remote peer is sending audio on each stream.
     361              :      */
     362              :     virtual std::map<std::string, bool> getRemoteAudioStreams() const = 0;
     363              : 
     364              : #ifdef ENABLE_VIDEO
     365              :     virtual void createSinks(ConfInfo& infos) = 0;
     366              : #endif
     367              : 
     368            0 :     virtual void switchInput(const std::string& = {}) {};
     369              : 
     370              :     /**
     371              :      * mute/unmute a media of a call
     372              :      * @param mediaType type of media
     373              :      * @param isMuted true for muting, false for unmuting
     374              :      */
     375              :     virtual void muteMedia(const std::string& mediaType, bool isMuted) = 0;
     376              : 
     377              :     /**
     378              :      * Send DTMF
     379              :      * @param code  The char code
     380              :      */
     381              :     virtual void carryingDTMFdigits(char code) = 0;
     382              : 
     383              :     /**
     384              :      * Make a change request of the current media with the provided media
     385              :      * @param mediaList the new media list
     386              :      * @return true on success
     387              :      */
     388              :     virtual bool requestMediaChange(const std::vector<libjami::MediaMap>& mediaList) = 0;
     389              : 
     390              :     /**
     391              :      * Retrieve current medias list
     392              :      * @return current medias
     393              :      */
     394              :     virtual std::vector<libjami::MediaMap> currentMediaList() const = 0;
     395              : 
     396              :     /**
     397              :      * Send a message to a call identified by its callid
     398              :      *
     399              :      * @param A list of mimetype/payload pairs
     400              :      * @param The sender of this message (could be another participant of a conference)
     401              :      */
     402              :     virtual void sendTextMessage(const std::map<std::string, std::string>& messages, const std::string& from) = 0;
     403              : 
     404              :     void onTextMessage(std::map<std::string, std::string>&& messages);
     405              : 
     406            0 :     virtual std::shared_ptr<SystemCodecInfo> getAudioCodec() const { return {}; }
     407            0 :     virtual std::shared_ptr<SystemCodecInfo> getVideoCodec() const { return {}; }
     408              : 
     409              :     virtual void restartMediaSender() = 0;
     410              : 
     411              :     // Media status methods
     412              :     virtual bool hasVideo() const = 0;
     413              :     virtual bool isCaptureDeviceMuted(const MediaType& mediaType) const = 0;
     414              : 
     415              :     /**
     416              :      * A Call can be in a conference. If this is the case, the other side
     417              :      * will send conference information describing the rendered image
     418              :      * @msg     A JSON object describing the conference
     419              :      */
     420              :     void setConferenceInfo(const std::string& msg);
     421              : 
     422              :     virtual void enterConference(std::shared_ptr<Conference> conference) = 0;
     423              :     virtual void exitConference() = 0;
     424              : 
     425            0 :     std::vector<std::map<std::string, std::string>> getConferenceInfos() const
     426              :     {
     427            0 :         return confInfo_.toVectorMapStringString();
     428              :     }
     429              : 
     430              :     std::unique_ptr<AudioDeviceGuard> audioGuard;
     431              :     void sendConfOrder(const Json::Value& root);
     432              :     void sendConfInfo(const std::string& json);
     433              :     void resetConfInfo();
     434              : 
     435              :     virtual void monitor() const = 0;
     436              : 
     437            6 :     int conferenceProtocolVersion() const { return peerConfProtocol_; }
     438              : 
     439              : protected:
     440              :     using clock = std::chrono::steady_clock;
     441              :     using time_point = clock::time_point;
     442              :     virtual void merge(Call& scall);
     443              : 
     444              :     /**
     445              :      * Constructor of a call
     446              :      * @param id Unique identifier of the call
     447              :      * @param type set definitely this call as incoming/outgoing
     448              :      */
     449              :     Call(const std::shared_ptr<Account>& account, const std::string& id, Call::CallType type);
     450              : 
     451              :     // TODO all these members are not protected against multi-thread access
     452              : 
     453              :     const std::string id_ {};
     454              : 
     455              :     ///< MultiDevice: parent call, nullptr otherwise. Access protected by callMutex_.
     456              :     mutable std::shared_ptr<Call> parent_;
     457              : 
     458              :     ///< MultiDevice: list of attached subcall
     459              :     SubcallSet subcalls_;
     460              : 
     461              :     using MsgList = std::list<std::pair<std::map<std::string, std::string>, std::string>>;
     462              : 
     463              :     ///< MultiDevice: message waiting to be sent (need a valid subcall)
     464              :     MsgList pendingOutMessages_;
     465              : 
     466              :     /** Protect every attribute that can be changed by two threads */
     467              :     mutable std::recursive_mutex callMutex_ {};
     468              : 
     469              :     mutable std::mutex confInfoMutex_ {};
     470              :     mutable ConfInfo confInfo_ {};
     471              :     time_point duration_start_ {time_point::min()};
     472              : 
     473              : private:
     474              :     bool validStateTransition(CallState newState);
     475              : 
     476              :     void checkPendingIM();
     477              : 
     478              :     void checkAudio();
     479              : 
     480              :     void subcallStateChanged(Call&, Call::CallState, Call::ConnectionState, int code);
     481              : 
     482              :     SubcallSet safePopSubcalls();
     483              : 
     484              :     std::vector<StateListenerCb> stateChangedListeners_ {};
     485              : 
     486              : protected:
     487              :     /** Unique conference ID, used exclusively in case of a conference */
     488              :     std::weak_ptr<Conference> conf_ {};
     489              : 
     490              :     /** Type of the call */
     491              :     CallType type_;
     492              : 
     493              :     /** Associate account ID */
     494              :     std::weak_ptr<Account> account_;
     495              : 
     496              :     /** Disconnected/Progressing/Trying/Ringing/Connected */
     497              :     ConnectionState connectionState_ {ConnectionState::DISCONNECTED};
     498              : 
     499              :     /** Inactive/Active/Hold/Busy/Error */
     500              :     CallState callState_ {CallState::INACTIVE};
     501              : 
     502              :     std::string reason_ {};
     503              : 
     504              :     /** Direct IP-to-IP or classic call */
     505              :     bool isIPToIP_ {false};
     506              : 
     507              :     /** Number of the peer */
     508              :     std::string peerNumber_ {};
     509              : 
     510              :     /** Peer Display Name */
     511              :     std::string peerDisplayName_ {};
     512              : 
     513              :     time_t timestamp_start_ {0};
     514              : 
     515              :     ///< MultiDevice: message received by subcall to merged yet
     516              :     MsgList pendingInMessages_;
     517              : 
     518              :     /// Supported conference protocol version
     519              :     int peerConfProtocol_ {0};
     520              :     std::string toUsername_ {};
     521              : 
     522              :     asio::steady_timer timeoutTimer_;
     523              : };
     524              : 
     525              : // Helpers
     526              : 
     527              : /**
     528              :  * Obtain a shared smart pointer of instance
     529              :  */
     530              : inline std::shared_ptr<Call>
     531          878 : getPtr(Call& call)
     532              : {
     533          878 :     return call.shared_from_this();
     534              : }
     535              : 
     536              : } // namespace jami
        

Generated by: LCOV version 2.0-1