123 |
andreas |
1 |
/*
|
|
|
2 |
* Copyright (C) 2022 by Andreas Theofilu <andreas@theosys.at>
|
|
|
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, write to the Free Software Foundation,
|
|
|
16 |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
17 |
*/
|
|
|
18 |
|
|
|
19 |
#ifndef __TSIPCLIENT_H__
|
|
|
20 |
#define __TSIPCLIENT_H__
|
|
|
21 |
|
|
|
22 |
#include <thread>
|
|
|
23 |
#include <atomic>
|
|
|
24 |
|
245 |
andreas |
25 |
#ifdef __aarch64__
|
|
|
26 |
#ifndef PJ_AUTOCONF
|
|
|
27 |
#define PJ_AUTOCONF 1
|
|
|
28 |
#endif
|
|
|
29 |
#endif
|
135 |
andreas |
30 |
#include <pjsua-lib/pjsua.h>
|
129 |
andreas |
31 |
|
|
|
32 |
#ifndef _NOSIP_
|
135 |
andreas |
33 |
#define SIP_MAX_LINES 2
|
124 |
andreas |
34 |
|
135 |
andreas |
35 |
#define current_acc pjsua_acc_get_default()
|
|
|
36 |
|
|
|
37 |
#define PJSUA_APP_NO_LIMIT_DURATION (int)0x7FFFFFFF
|
|
|
38 |
// Ringtones US UK
|
|
|
39 |
#define RINGBACK_FREQ1 440 // 400
|
|
|
40 |
#define RINGBACK_FREQ2 480 // 450
|
|
|
41 |
#define RINGBACK_ON 2000 // 400
|
|
|
42 |
#define RINGBACK_OFF 4000 // 200
|
|
|
43 |
#define RINGBACK_CNT 1 // 2
|
|
|
44 |
#define RINGBACK_INTERVAL 4000 // 2000
|
|
|
45 |
|
|
|
46 |
#define RING_FREQ1 800
|
|
|
47 |
#define RING_FREQ2 640
|
|
|
48 |
#define RING_ON 200
|
|
|
49 |
#define RING_OFF 100
|
|
|
50 |
#define RING_CNT 3
|
|
|
51 |
#define RING_INTERVAL 3000
|
|
|
52 |
|
|
|
53 |
typedef unsigned int uint_t;
|
|
|
54 |
|
123 |
andreas |
55 |
class TSIPClient
|
|
|
56 |
{
|
|
|
57 |
public:
|
124 |
andreas |
58 |
typedef enum SIP_STATE_t
|
|
|
59 |
{
|
127 |
andreas |
60 |
SIP_NONE, // Undefined state (only valid on startup before initialisation)
|
|
|
61 |
SIP_IDLE, // Initialized but no event
|
|
|
62 |
SIP_CONNECTED, // Call is in progress
|
|
|
63 |
SIP_DISCONNECTED, // Call has ended
|
|
|
64 |
SIP_TRYING, // Trying to call someone
|
|
|
65 |
SIP_RINGING, // Phone is ringing (incoming call)
|
|
|
66 |
SIP_HOLD, // Active call is paused
|
|
|
67 |
SIP_REJECTED, // Outgoing call was rejected
|
|
|
68 |
SIP_ERROR // An error occured
|
124 |
andreas |
69 |
}SIP_STATE_t;
|
|
|
70 |
|
127 |
andreas |
71 |
TSIPClient();
|
123 |
andreas |
72 |
~TSIPClient();
|
|
|
73 |
|
135 |
andreas |
74 |
bool init();
|
|
|
75 |
|
123 |
andreas |
76 |
void cleanUp();
|
124 |
andreas |
77 |
int getLineID() { return mLine; }
|
138 |
andreas |
78 |
SIP_STATE_t getSIPState(pjsua_call_id id) { if (id >= 0 && id < PJSUA_MAX_CALLS) return mSIPState[id]; else return SIP_NONE; }
|
135 |
andreas |
79 |
bool isRegistered() { return mRegistered; }
|
141 |
andreas |
80 |
static TSIPClient *getSIPClient() { return mMyself; }
|
123 |
andreas |
81 |
|
127 |
andreas |
82 |
bool call(const std::string& dest); //<! Start a phone call
|
135 |
andreas |
83 |
bool pickup(pjsua_call_id call); //<! Lift up if the phone is ringing
|
127 |
andreas |
84 |
bool terminate(int id); //<! Terminate a call
|
|
|
85 |
bool hold(int id); //<! Pause a call
|
|
|
86 |
bool resume(int id); //<! Resume a paused call
|
|
|
87 |
bool sendDTMF(std::string& dtmf); //<! Send a DTMF string
|
128 |
andreas |
88 |
bool sendLinestate(); //<! Queries the state of each of the connections used by the SIP device.
|
|
|
89 |
bool sendPrivate(bool state); //<! Enables or disables the privacy feature on the phone (do not disturb).
|
|
|
90 |
bool redial(); //<! Redial last number
|
|
|
91 |
bool transfer(int id, const std::string& num); //<! transfer the current call
|
138 |
andreas |
92 |
bool setDTMFduration(uint_t ms); //<! Set the DTMF duration in ms.
|
|
|
93 |
bool getPrivate() { return mDoNotDisturb; } //<! Returns the current private mode.
|
144 |
andreas |
94 |
bool sendIM(const std::string& target, const std::string& msg); //<! Send an IM to somebody or to the peer of the current call
|
|
|
95 |
size_t getNumberMessages() { return mMessages.size(); }
|
123 |
andreas |
96 |
|
|
|
97 |
protected:
|
141 |
andreas |
98 |
void runRinger(); //<! Plays a ring tone if a call is coming.
|
123 |
andreas |
99 |
|
135 |
andreas |
100 |
static void _log_call(int level, const char *data, int len);
|
|
|
101 |
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata);
|
|
|
102 |
static void on_call_state(pjsua_call_id call_id, pjsip_event *e);
|
|
|
103 |
static void on_call_media_state(pjsua_call_id call_id);
|
|
|
104 |
static void call_on_dtmf_callback2(pjsua_call_id call_id, const pjsua_dtmf_info *info);
|
|
|
105 |
static pjsip_redirect_op call_on_redirected(pjsua_call_id call_id, const pjsip_uri *target, const pjsip_event *e);
|
|
|
106 |
static void on_call_transfer_status(pjsua_call_id call_id, int status_code, const pj_str_t *status_text, pj_bool_t final, pj_bool_t *p_cont);
|
|
|
107 |
static void on_transport_state(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info);
|
|
|
108 |
static void on_ip_change_progress(pjsua_ip_change_op op, pj_status_t status, const pjsua_ip_change_op_info *info);
|
|
|
109 |
static void simple_registrar(pjsip_rx_data *rdata);
|
|
|
110 |
static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata);
|
|
|
111 |
static void ringback_start(pjsua_call_id call_id);
|
|
|
112 |
static void ring_start(pjsua_call_id call_id);
|
|
|
113 |
static void ring_stop(pjsua_call_id call_id);
|
|
|
114 |
static pj_bool_t find_next_call(void);
|
|
|
115 |
static void call_timeout_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry);
|
|
|
116 |
static void on_playfile_done(pjmedia_port *port, void *usr_data);
|
|
|
117 |
static void hangup_timeout_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry);
|
144 |
andreas |
118 |
static void on_pager2(pjsua_call_id call_id, const pj_str_t *from,
|
|
|
119 |
const pj_str_t *to, const pj_str_t *contact,
|
|
|
120 |
const pj_str_t *mime_type, const pj_str_t *body,
|
|
|
121 |
pjsip_rx_data *rdata, pjsua_acc_id acc_id);
|
|
|
122 |
static void on_buddy_state(pjsua_buddy_id buddy_id);
|
|
|
123 |
static void on_buddy_evsub_state(pjsua_buddy_id buddy_id, pjsip_evsub *sub, pjsip_event *event);
|
|
|
124 |
static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info);
|
135 |
andreas |
125 |
|
123 |
andreas |
126 |
private:
|
144 |
andreas |
127 |
typedef struct _uri_t
|
|
|
128 |
{
|
|
|
129 |
std::string name; //!< The name coming in double quotes (optional)
|
|
|
130 |
std::string scheme; //!< The scheme (e.g. sip)
|
|
|
131 |
std::string user; //!< The user name / telephone number
|
|
|
132 |
std::string server; //!< The IP address or FQDN of the server
|
|
|
133 |
int port; //!< An optional port number
|
|
|
134 |
}_uri_t;
|
|
|
135 |
|
135 |
andreas |
136 |
static pjsip_module mod_default_handler;
|
|
|
137 |
|
127 |
andreas |
138 |
int getNumberCalls();
|
138 |
andreas |
139 |
pjsua_call_id getActiveCall();
|
|
|
140 |
void setSIPState(SIP_STATE_t s, pjsua_call_id id) { if (id >= 0 && id < PJSUA_MAX_CALLS) mSIPState[id] = s; }
|
127 |
andreas |
141 |
|
135 |
andreas |
142 |
static void sendConnectionStatus(SIP_STATE_t state, int id);
|
141 |
andreas |
143 |
static void init_ringtone_player();
|
|
|
144 |
static pj_status_t start_ring_tone();
|
|
|
145 |
static pj_status_t stop_ring_tone();
|
144 |
andreas |
146 |
static pjsua_buddy_id addBuddy(const std::string& rsipurl);
|
|
|
147 |
static _uri_t parseUri(const std::string& uri);
|
138 |
andreas |
148 |
|
124 |
andreas |
149 |
int mLine{0};
|
135 |
andreas |
150 |
bool mRegistered{false};
|
138 |
andreas |
151 |
SIP_STATE_t mSIPState[PJSUA_MAX_CALLS];
|
135 |
andreas |
152 |
pjsua_acc_id mAccountID{0};
|
|
|
153 |
std::string mLastCall;
|
138 |
andreas |
154 |
uint_t mDTMFduration{PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT};
|
|
|
155 |
bool mDoNotDisturb{false};
|
144 |
andreas |
156 |
std::vector<std::string>mMessages; // Holds all received messages
|
123 |
andreas |
157 |
|
135 |
andreas |
158 |
static TSIPClient *mMyself;
|
|
|
159 |
static pjsua_call_id mCurrentCall;
|
|
|
160 |
static std::atomic<bool> mRefreshRun;
|
141 |
andreas |
161 |
static bool mPhoneRingInit;
|
135 |
andreas |
162 |
// Here is the configuration for the PJSUA library
|
|
|
163 |
typedef struct app_call_data
|
|
|
164 |
{
|
|
|
165 |
pj_timer_entry timer;
|
|
|
166 |
pj_bool_t ringback_on{PJ_FALSE};
|
|
|
167 |
pj_bool_t ring_on{PJ_FALSE};
|
|
|
168 |
} app_call_data;
|
123 |
andreas |
169 |
|
141 |
andreas |
170 |
typedef struct ringtone_port_info_t
|
|
|
171 |
{
|
|
|
172 |
pj_bool_t ring_on{PJ_FALSE};
|
|
|
173 |
int ring_slot{0};
|
|
|
174 |
pjmedia_port *ring_port{nullptr};
|
|
|
175 |
pj_pool_t *pool{nullptr};
|
|
|
176 |
} ringtone_port_info_t;
|
|
|
177 |
|
135 |
andreas |
178 |
typedef struct pjsua_app_config
|
|
|
179 |
{
|
|
|
180 |
pjsua_config cfg;
|
|
|
181 |
pjsua_logging_config log_cfg;
|
|
|
182 |
pjsua_media_config media_cfg;
|
|
|
183 |
pj_bool_t no_refersub{PJ_FALSE};
|
|
|
184 |
pj_bool_t enable_qos{PJ_FALSE};
|
|
|
185 |
pj_bool_t no_tcp{PJ_TRUE};
|
|
|
186 |
pj_bool_t no_udp{PJ_FALSE};
|
|
|
187 |
pj_bool_t use_tls{PJ_FALSE};
|
|
|
188 |
pjsua_transport_config udp_cfg;
|
|
|
189 |
pjsua_transport_config rtp_cfg;
|
|
|
190 |
pjsip_redirect_op redir_op;
|
|
|
191 |
int srtp_keying{0};
|
|
|
192 |
|
|
|
193 |
uint_t acc_cnt{0};
|
|
|
194 |
pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
|
|
|
195 |
|
|
|
196 |
uint_t buddy_cnt{0};
|
|
|
197 |
pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
|
|
|
198 |
|
|
|
199 |
app_call_data call_data[PJSUA_MAX_CALLS];
|
|
|
200 |
|
|
|
201 |
pj_pool_t *pool{nullptr};
|
|
|
202 |
/* Compatibility with older pjsua */
|
|
|
203 |
|
|
|
204 |
uint_t codec_cnt{0};
|
|
|
205 |
pj_str_t codec_arg[32];
|
|
|
206 |
uint_t codec_dis_cnt{0};
|
|
|
207 |
pj_str_t codec_dis[32];
|
|
|
208 |
pj_bool_t null_audio{PJ_FALSE};
|
141 |
andreas |
209 |
uint_t wav_count{0};
|
135 |
andreas |
210 |
pj_str_t wav_files[32];
|
|
|
211 |
uint_t tone_count{0};
|
|
|
212 |
pjmedia_tone_desc tones[32];
|
|
|
213 |
pjsua_conf_port_id tone_slots[32];
|
|
|
214 |
pjsua_player_id wav_id{0};
|
|
|
215 |
pjsua_conf_port_id wav_port{0};
|
|
|
216 |
pj_bool_t auto_play{PJ_FALSE};
|
|
|
217 |
pj_bool_t auto_play_hangup{PJ_FALSE};
|
|
|
218 |
pj_timer_entry auto_hangup_timer;
|
|
|
219 |
pj_bool_t auto_loop{PJ_FALSE};
|
|
|
220 |
pj_bool_t auto_conf{PJ_FALSE};
|
|
|
221 |
pj_str_t rec_file;
|
|
|
222 |
pj_bool_t auto_rec{PJ_FALSE};
|
|
|
223 |
pjsua_recorder_id rec_id{0};
|
|
|
224 |
pjsua_conf_port_id rec_port{0};
|
|
|
225 |
unsigned auto_answer{0};
|
141 |
andreas |
226 |
unsigned duration{36000};
|
|
|
227 |
float mic_level{100.0};
|
|
|
228 |
float speaker_level{100.0};
|
135 |
andreas |
229 |
|
|
|
230 |
int capture_dev{0};
|
|
|
231 |
int playback_dev{0};
|
|
|
232 |
uint_t capture_lat{0};
|
|
|
233 |
uint_t playback_lat{0};
|
|
|
234 |
|
|
|
235 |
pj_bool_t no_tones{PJ_FALSE};
|
|
|
236 |
int ringback_slot{0};
|
|
|
237 |
int ringback_cnt{0};
|
|
|
238 |
pjmedia_port *ringback_port{nullptr};
|
|
|
239 |
int ring_slot{0};
|
|
|
240 |
int ring_cnt{0};
|
|
|
241 |
pjmedia_port *ring_port{nullptr};
|
|
|
242 |
|
|
|
243 |
uint_t aud_cnt{0};
|
|
|
244 |
}pjsua_app_config;
|
|
|
245 |
|
|
|
246 |
static pjsua_app_config mAppConfig;
|
141 |
andreas |
247 |
static ringtone_port_info_t mRingtonePortInfo;
|
124 |
andreas |
248 |
};
|
129 |
andreas |
249 |
#endif // _NOSIP_
|
|
|
250 |
#endif // __TSIPCLIENT_H__
|