Skip to content

Instantly share code, notes, and snippets.

@mikelnrd
Last active October 4, 2016 10:26
Show Gist options
  • Save mikelnrd/6b8b4986d6529ae751ebd2af5bf4d29f to your computer and use it in GitHub Desktop.
Save mikelnrd/6b8b4986d6529ae751ebd2af5bf4d29f to your computer and use it in GitHub Desktop.
//!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!//////!!//////!!//////!!//////!!//
// Scroll down in this gist to see the run logs and the full entire app as this is just the key snippet where I've likely made a mistake! Thanks :)
//!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!////!!//////!!//////!!//////!!//////!!//
/////////////////////////////////////////////////////////////////////////////////
// key piece of code
/////////////////////////////////////////////////////////////////////////////////
struct call_data {
pj_pool_t *pool;
pjmedia_conf *conf;
pjmedia_port *cport;
pjmedia_port *null;
pjmedia_port *writer;
pjmedia_port *player;
pjmedia_master_port *m;
unsigned int call_slot;
unsigned int writer_slot;
unsigned int player_slot;
};
static void call_media_init(pjsua_call_id call_id){
log_message("RUNNING... call_media_init\n");
pj_pool_t *pool;
struct call_data *cd;
pj_status_t status;
pool = pjsua_pool_create("mycall", 4000, 4000);
cd = PJ_POOL_ZALLOC_T(pool, struct call_data);
cd->pool = pool;
pjsua_call_set_user_data(call_id, (void*)cd);
pjsua_media_config media_cfg;
pjsua_media_config_default(&media_cfg);
status = pjmedia_conf_create(
cd->pool,
media_cfg.max_media_ports, //max media ports
media_cfg.clock_rate,
media_cfg.channel_count,
media_cfg.clock_rate * media_cfg.channel_count * media_cfg.audio_frame_ptime / 1000, //mconf_cfg.samples_per_frame,
16, //mconf_cfg.bits_per_sample,
PJMEDIA_CONF_NO_DEVICE | PJMEDIA_CONF_NO_MIC, //options
&cd->conf //pointer to conference bridge instance
);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
cd->cport = pjmedia_conf_get_master_port(cd->conf);
status = pjmedia_null_port_create(
cd->pool,
media_cfg.clock_rate,
media_cfg.channel_count,
media_cfg.clock_rate * media_cfg.channel_count * media_cfg.audio_frame_ptime / 1000, //mconf_cfg.samples_per_frame,
16, //mconf_cfg.bits_per_sample,
&cd->null);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_master_port_create(cd->pool, cd->null, cd->cport, 0, &cd->m);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_master_port_start(cd->m);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
//todo(mike) handle errors, see pjsua_aud.c
/* wav writer */
status = pjmedia_wav_writer_port_create(
cd->pool,
"testingtesting.wav", //path
media_cfg.clock_rate,
media_cfg.channel_count,
media_cfg.clock_rate * media_cfg.channel_count * media_cfg.audio_frame_ptime / 1000, //mconf_cfg.samples_per_frame,
16, //mconf_cfg.bits_per_sample,
0, //options
0, //buf_size defaults to 4kb if set to 0
&cd->writer //yes this should be a pjmedia_port **
);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_conf_add_port(cd->conf, cd->pool, cd->writer, NULL, &cd->writer_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);
pjmedia_port_destroy(cd->writer);
}
pjmedia_conf_connect_port(cd->conf, cd->call_slot, cd->writer_slot, 0);
/* wav player */
status = pjmedia_wav_player_port_create(
cd->pool,
"message.wav",
media_cfg.audio_frame_ptime,
0,
0,
&cd->player //yes this should be a pjmedia_port **
);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_conf_add_port(cd->conf, cd->pool, cd->player, NULL, &cd->player_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);
pjmedia_port_destroy(cd->player);
}
pjmedia_conf_connect_port(cd->conf, cd->player_slot, cd->call_slot, 0);
//uncomment to loop back remote audio (also doesn't work)
//pjmedia_conf_connect_port(cd->conf, cd->call_slot, cd->call_slot, 0);
}
static void call_media_deinit(pjsua_call_id call_id){
log_message("RUNNING... call_media_deinit\n");
struct call_data *cd;
cd = (struct call_data*) pjsua_call_get_user_data(call_id);
if (!cd)
return;
pjmedia_master_port_stop(cd->m);
pjmedia_master_port_destroy(cd->m, PJ_FALSE);
pjmedia_conf_destroy(cd->conf);
pjmedia_port_destroy(cd->null);
pjmedia_port_destroy(cd->writer);
log_message("Called call_media_deinit.\n");
pjsua_call_set_user_data(call_id, NULL);
}
09:59:05.474 os_core_unix.c !pjlib 2.5.5 for POSIX initialized
09:59:05.490 sip_endpoint.c .Creating endpoint instance...
09:59:05.516 pjlib .select() I/O Queue created (0x26aaae0)
09:59:05.517 sip_endpoint.c .Module "mod-msg-print" registered
09:59:05.517 sip_transport. .Transport manager created.
09:59:05.517 pjsua_core.c .PJSUA state changed: NULL --> CREATED
09:59:05.517 sip_endpoint.c .Module "mod-pjsua-log" registered
09:59:05.517 sip_endpoint.c .Module "mod-tsx-layer" registered
09:59:05.517 sip_endpoint.c .Module "mod-stateful-util" registered
09:59:05.517 sip_endpoint.c .Module "mod-ua" registered
09:59:05.517 sip_endpoint.c .Module "mod-100rel" registered
09:59:05.517 sip_endpoint.c .Module "mod-pjsua" registered
09:59:05.528 sip_endpoint.c .Module "mod-invite" registered
09:59:05.528 pjlib ..select() I/O Queue created (0x26b6a78)
09:59:05.531 sip_endpoint.c .Module "mod-evsub" registered
09:59:05.531 sip_endpoint.c .Module "mod-presence" registered
09:59:05.531 sip_endpoint.c .Module "mod-mwi" registered
09:59:05.531 sip_endpoint.c .Module "mod-refer" registered
09:59:05.531 sip_endpoint.c .Module "mod-pjsua-pres" registered
09:59:05.531 sip_endpoint.c .Module "mod-pjsua-im" registered
09:59:05.531 sip_endpoint.c .Module "mod-pjsua-options" registered
09:59:05.531 pjsua_core.c .1 SIP worker threads created
09:59:05.531 pjsua_core.c .pjsua version 2.5.5 for Linux-4.4.20/x86_64/glibc-2.19 initialized
09:59:05.531 pjsua_core.c .PJSUA state changed: CREATED --> INIT
09:59:05.532 pjsua_core.c SIP UDP socket reachable at 172.XX.X.X:5060
09:59:05.532 udp0x26c52b0 SIP UDP transport started, published address is 172.XX.X.X:5060
09:59:05.532 pjsua_core.c PJSUA state changed: INIT --> STARTING
09:59:05.532 sip_endpoint.c .Module "mod-unsolicited-mwi" registered
09:59:05.533 pjsua_core.c .PJSUA state changed: STARTING --> RUNNING
09:59:05.533 pjsua_acc.c Adding account: id=sip:mike@example.com
09:59:05.533 pjsua_acc.c .Account sip:mike@example.com added with id 0
09:59:05.533 pjsua_call.c Making call with acc #0 to sip:+447468XXXXXX@XXXXXX.pstn.twilio.com
09:59:05.533 pjsua_media.c .Call 0: initializing media..
09:59:05.533 pjsua_media.c ..RTP socket reachable at 172.XX.X.X:4000
09:59:05.533 pjsua_media.c ..RTCP socket reachable at 172.XX.X.X:4001
09:59:05.533 pjsua_media.c ..Media index 0 selected for audio call 0
09:59:05.562 pjsua_core.c ....TX 918 bytes Request msg INVITE/cseq=16428 (tdta0x26d0b60) to UDP 54.XXX.XX.X:5060:
INVITE sip:+447468XXXXXX@XXXXXX.pstn.twilio.com SIP/2.0
Via: SIP/2.0/UDP 172.XX.X.X:5060;rport;branch=z9hG4bKPj2741734a-4b02-4331-871b-cb41d745aa8e
Max-Forwards: 70
From: sip:mike@example.com;tag=75982d61-bd17-4567-8d20-af80f715ac2e
To: sip:+447468XXXXXX@XXXXXX.pstn.twilio.com
Contact: <sip:mike@172.XX.X.X:5060;ob>
Call-ID: cad2b1f2-08a4-43b9-adfa-63a7af1250b3
CSeq: 16428 INVITE
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
Supported: replaces, 100rel, timer, norefersub
Session-Expires: 1800
Min-SE: 90
Content-Type: application/sdp
Content-Length: 289
v=0
o=- 3684563945 3684563945 IN IP4 172.XX.X.X
s=pjmedia
b=AS:84
t=0 0
a=X-nat:0
m=audio 4000 RTP/AVP 0 8 96
c=IN IP4 172.XX.X.X
b=TIAS:64000
a=rtcp:4001 IN IP4 172.XX.X.X
a=sendrecv
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:96 telephone-event/8000
a=fmtp:96 0-16
--end msg--
09:59:05.565 APP .......Call 0 state=CALLING
09:59:05.572 wav_writer.c .......File writer 'testingtesting.wav' created: samp.rate=16000, bufsize=4KB
09:59:05.572 conference.c .......Port 0 (Master/sound) transmitting to port 1 (testingtesting.wav)
09:59:05.579 wav_player.c .......File player 'message.wav' created: samp.rate=16000, ch=1, bufsize=4KB, filesize=56KB
09:59:05.579 conference.c .......Port 2 (message.wav) transmitting to port 0 (Master/sound)
Press 'h' to hangup all calls, 'q' to quit
09:59:05.990 pjsua_core.c .RX 374 bytes Response msg 100/INVITE/cseq=16428 (rdata0x26c6d38) from UDP 54.XXX.XX.X:5060:
SIP/2.0 100 Giving a try
Via: SIP/2.0/UDP 172.XX.X.X:5060;received=82.X.XX.XXX;rport=57477;branch=z9hG4bKPj2741734a-4b02-4331-871b-cb41d745aa8e
From: sip:mike@example.com;tag=75982d61-bd17-4567-8d20-af80f715ac2e
To: sip:+447468XXXXXX@XXXXXX.pstn.twilio.com
Call-ID: cad2b1f2-08a4-43b9-adfa-63a7af1250b3
CSeq: 16428 INVITE
Server: Twilio Gateway
Content-Length: 0
--end msg--
09:59:10.230 pjsua_core.c .RX 945 bytes Response msg 183/INVITE/cseq=16428 (rdata0x7fb4b8000908) from UDP 54.XXX.XX.X:5060:
SIP/2.0 183 Session progress
CSeq: 16428 INVITE
Call-ID: cad2b1f2-08a4-43b9-adfa-63a7af1250b3
From: <sip:mike@example.com>;tag=75982d61-bd17-4567-8d20-af80f715ac2e
To: <sip:+447468XXXXXX@XXXXXX.pstn.twilio.com>;tag=96764593_6772d868_ea2d552b-fe2b-4932-90e9-c559976ed365
Via: SIP/2.0/UDP 172.XX.X.X:5060;received=82.X.XX.XXX;rport=57477;branch=z9hG4bKPj2741734a-4b02-4331-871b-cb41d745aa8e
Record-Route: <sip:54.XXX.XX.X:5060;lr;ftag=75982d61-bd17-4567-8d20-af80f715ac2e;twnat=sip:82.X.XX.XXX:57477>
Server: Twilio
Contact: <sip:172.XX.X.XXX:5060>
Content-Type: application/sdp
X-Twilio-CallSid: CA5f210920c6XXXXXXaa4e4bcf5fXXXXXX
Content-Length: 280
v=0
o=- 863029107 863029107 IN IP4 54.XXX.XX.XXX
s=Twilio Media Gateway
c=IN IP4 54.XXX.XX.XXX
t=0 0
m=audio 10060 RTP/AVP 0 96
a=rtpmap:0 PCMU/8000
a=rtpmap:96 telephone-event/8000
a=fmtp:96 0-15
a=maxptime:20
a=ptime:20
a=sendrecv
a=rtcp:10061 IN IP4 54.XXX.XX.XXX
--end msg--
09:59:10.230 APP .....Call 0 state=EARLY
09:59:10.230 pjsua_media.c .....Call 0: updating media..
09:59:10.230 pjsua_aud.c ......Audio channel update..
09:59:10.230 strm0x7fb4b800 .......VAD temporarily disabled
09:59:10.232 strm0x7fb4b800 .......Encoder stream started
09:59:10.233 strm0x7fb4b800 .......Decoder stream started
09:59:10.233 pjsua_media.c ......Audio updated, stream #0: PCMU (sendrecv)
09:59:10.867 strm0x7fb4b800 !VAD re-enabled
09:59:12.876 pjsua_core.c .RX 623 bytes Response msg 180/INVITE/cseq=16428 (rdata0x7fb4b8000908) from UDP 54.XXX.XX.X:5060:
SIP/2.0 180 Ringing
CSeq: 16428 INVITE
Call-ID: cad2b1f2-08a4-43b9-adfa-63a7af1250b3
From: <sip:mike@example.com>;tag=75982d61-bd17-4567-8d20-af80f715ac2e
To: <sip:+447468XXXXXX@XXXXXX.pstn.twilio.com>;tag=96764593_6772d868_ea2d552b-fe2b-4932-90e9-c559976ed365
Via: SIP/2.0/UDP 172.XX.X.X:5060;received=82.X.XX.XXX;rport=57477;branch=z9hG4bKPj2741734a-4b02-4331-871b-cb41d745aa8e
Record-Route: <sip:54.XXX.XX.X:5060;lr;ftag=75982d61-bd17-4567-8d20-af80f715ac2e;twnat=sip:82.X.XX.XXX:57477>
Server: Twilio
Contact: <sip:172.XX.X.XXX:5060>
X-Twilio-CallSid: CA5f210920c6XXXXXXaa4e4bcf5fXXXXXX
Content-Length: 0
--end msg--
09:59:12.876 APP .....Call 0 state=EARLY
09:59:14.227 pjsua_core.c .RX 931 bytes Response msg 200/INVITE/cseq=16428 (rdata0x7fb4b8000908) from UDP 54.XXX.XX.X:5060:
SIP/2.0 200 OK
CSeq: 16428 INVITE
Call-ID: cad2b1f2-08a4-43b9-adfa-63a7af1250b3
From: <sip:mike@example.com>;tag=75982d61-bd17-4567-8d20-af80f715ac2e
To: <sip:+447468XXXXXX@XXXXXX.pstn.twilio.com>;tag=96764593_6772d868_ea2d552b-fe2b-4932-90e9-c559976ed365
Via: SIP/2.0/UDP 172.XX.X.X:5060;received=82.X.XX.XXX;rport=57477;branch=z9hG4bKPj2741734a-4b02-4331-871b-cb41d745aa8e
Record-Route: <sip:54.XXX.XX.X:5060;lr;ftag=75982d61-bd17-4567-8d20-af80f715ac2e;twnat=sip:82.X.XX.XXX:57477>
Server: Twilio
Contact: <sip:172.XX.X.XXX:5060>
Content-Type: application/sdp
X-Twilio-CallSid: CA5f210920c6XXXXXXaa4e4bcf5fXXXXXX
Content-Length: 280
v=0
o=- 950195842 950195842 IN IP4 54.XXX.XX.XXX
s=Twilio Media Gateway
c=IN IP4 54.XXX.XX.XXX
t=0 0
m=audio 10060 RTP/AVP 0 96
a=rtpmap:0 PCMU/8000
a=rtpmap:96 telephone-event/8000
a=fmtp:96 0-15
a=maxptime:20
a=ptime:20
a=sendrecv
a=rtcp:10061 IN IP4 54.XXX.XX.XXX
--end msg--
09:59:14.227 APP .....Call 0 state=CONNECTING
09:59:14.227 inv0x26c9548 ....SDP negotiation done, message body is ignored
09:59:14.228 pjsua_core.c .....TX 511 bytes Request msg ACK/cseq=16428 (tdta0x7fb4b8009db0
./myapp sip:+447468XXXXXX@XXXXXX.pstn.twilio.com >> log.txt
WARNING: no real random source present!
RUNNING... on_call_state
Call state is CALLING.
RUNNING... call_media_init
RUNNING... on_call_state
RUNNING... on_stream_created
RUNNING... on_call_media_state
Media state is ACTIVE.
RUNNING... on_call_state
RUNNING... on_call_state
RUNNING... on_call_state
Call state is CONFIRMED.
RUNNING... on_call_state
Call state is DISCONNECTED.
RUNNING... call_media_deinit
Called call_media_deinit.
RUNNING... on_stream_destroyed
^C
#pragma GCC diagnostic ignored "-Wwrite-strings"
#include <pjlib.h>
#include <pjlib-util.h>
#include <pjmedia.h>
#include <pjmedia-codec.h>
#include <pjsip.h>
#include <pjsip_simple.h>
#include <pjsip_ua.h>
#include <pjsua-lib/pjsua.h>
#define THIS_FILE "APP"
#define SIP_DOMAIN "example.com"
#define SIP_USER "mike"
#define SIP_PASSWD "secret"
// global helper vars
int app_exiting = 0;
// struct for app configuration settings
struct app_config {
char *sip_domain;
char *sip_user;
char *sip_password;
char *phone_number;
char *tts;
char *tts_file;
int record_call;
char *record_file;
int repetition_limit;
int silent_mode;
} app_cfg;
// helper for logging messages to console (disabled if silent mode is active)
static void log_message(char *message)
{
if (!app_cfg.silent_mode)
{
fprintf(stderr, message);
}
}
// clean application exit
static void app_exit(char *message)
{
log_message(message);
if (!app_exiting)
{
app_exiting = 1;
log_message("Stopping application ... ");
// hangup open calls and stop pjsua
pjsua_call_hangup_all();
pjsua_destroy();
log_message("Done.\n");
exit(0);
}
}
// display error and exit application
static void error_exit(const char *title, pj_status_t status)
{
if (!app_exiting)
{
app_exiting = 1;
pjsua_perror("SIP Call", title, status);
// hangup open calls and stop pjsua
pjsua_call_hangup_all();
pjsua_destroy();
exit(1);
}
}
/////////////////////////////////////////////////////////////////////////////////
// key piece of code
/////////////////////////////////////////////////////////////////////////////////
struct call_data {
pj_pool_t *pool;
pjmedia_conf *conf;
pjmedia_port *cport;
pjmedia_port *null;
pjmedia_port *writer;
pjmedia_port *player;
pjmedia_master_port *m;
unsigned int call_slot;
unsigned int writer_slot;
unsigned int player_slot;
};
static void call_media_init(pjsua_call_id call_id){
log_message("RUNNING... call_media_init\n");
pj_pool_t *pool;
struct call_data *cd;
pj_status_t status;
pool = pjsua_pool_create("mycall", 4000, 4000);
cd = PJ_POOL_ZALLOC_T(pool, struct call_data);
cd->pool = pool;
pjsua_call_set_user_data(call_id, (void*)cd);
pjsua_media_config media_cfg;
pjsua_media_config_default(&media_cfg);
status = pjmedia_conf_create(
cd->pool,
media_cfg.max_media_ports, //max media ports
media_cfg.clock_rate,
media_cfg.channel_count,
media_cfg.clock_rate * media_cfg.channel_count * media_cfg.audio_frame_ptime / 1000, //mconf_cfg.samples_per_frame,
16, //mconf_cfg.bits_per_sample,
PJMEDIA_CONF_NO_DEVICE | PJMEDIA_CONF_NO_MIC, //options
&cd->conf //pointer to conference bridge instance
);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
cd->cport = pjmedia_conf_get_master_port(cd->conf);
status = pjmedia_null_port_create(
cd->pool,
media_cfg.clock_rate,
media_cfg.channel_count,
media_cfg.clock_rate * media_cfg.channel_count * media_cfg.audio_frame_ptime / 1000, //mconf_cfg.samples_per_frame,
16, //mconf_cfg.bits_per_sample,
&cd->null);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_master_port_create(cd->pool, cd->null, cd->cport, 0, &cd->m);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_master_port_start(cd->m);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
//todo(mike) handle errors, see pjsua_aud.c
/* wav writer */
status = pjmedia_wav_writer_port_create(
cd->pool,
"testingtesting.wav", //path
media_cfg.clock_rate,
media_cfg.channel_count,
media_cfg.clock_rate * media_cfg.channel_count * media_cfg.audio_frame_ptime / 1000, //mconf_cfg.samples_per_frame,
16, //mconf_cfg.bits_per_sample,
0, //options
0, //buf_size defaults to 4kb if set to 0
&cd->writer //yes this should be a pjmedia_port **
);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_conf_add_port(cd->conf, cd->pool, cd->writer, NULL, &cd->writer_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);
pjmedia_port_destroy(cd->writer);
}
pjmedia_conf_connect_port(cd->conf, cd->call_slot, cd->writer_slot, 0);
/* wav player */
status = pjmedia_wav_player_port_create(
cd->pool,
"message.wav",
media_cfg.audio_frame_ptime,
0,
0,
&cd->player //yes this should be a pjmedia_port **
);
if (status != PJ_SUCCESS) {pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);}
status = pjmedia_conf_add_port(cd->conf, cd->pool, cd->player, NULL, &cd->player_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "STATUS ERROR: ", status);
pjmedia_port_destroy(cd->player);
}
pjmedia_conf_connect_port(cd->conf, cd->player_slot, cd->call_slot, 0);
//uncomment to loop back remote audio (also doesn't work)
//pjmedia_conf_connect_port(cd->conf, cd->call_slot, cd->call_slot, 0);
}
static void call_media_deinit(pjsua_call_id call_id){
log_message("RUNNING... call_media_deinit\n");
struct call_data *cd;
cd = (struct call_data*) pjsua_call_get_user_data(call_id);
if (!cd)
return;
pjmedia_master_port_stop(cd->m);
pjmedia_master_port_destroy(cd->m, PJ_FALSE);
pjmedia_conf_destroy(cd->conf);
pjmedia_port_destroy(cd->null);
pjmedia_port_destroy(cd->writer);
log_message("Called call_media_deinit.\n");
pjsua_call_set_user_data(call_id, NULL);
}
/////////////////////////////////////////////////////////////////////////////////
// on_ functions
/////////////////////////////////////////////////////////////////////////////////
static void on_call_state(pjsua_call_id call_id, pjsip_event *e){
log_message("RUNNING... on_call_state\n");
pjsua_call_info call_info;
pjsua_call_get_info(call_id, &call_info);
PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id,
(int)call_info.state_text.slen,
call_info.state_text.ptr));
if (call_info.state == PJSIP_INV_STATE_CALLING) {
log_message("Call state is CALLING.\n");
call_media_init(call_id);
}
if (call_info.state == PJSIP_INV_STATE_CONFIRMED) {
log_message("Call state is CONFIRMED.\n");
}
if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
log_message("Call state is DISCONNECTED.\n");
call_media_deinit(call_id);
}
}
static void on_call_media_state(pjsua_call_id call_id){
log_message("RUNNING... on_call_media_state\n");
pjsua_call_info call_info;
pjsua_call_get_info(call_id, &call_info);
if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
log_message("Media state is ACTIVE.\n");
//call_media_init(call_id); //doesn't work here either!
}
}
static void on_stream_created(pjsua_call_id call_id,
pjmedia_stream *strm,
unsigned stream_idx,
pjmedia_port **p_port){
log_message("RUNNING... on_stream_created\n");
struct call_data *cd;
cd = (struct call_data*) pjsua_call_get_user_data(call_id);
if (!cd)
return;
pjmedia_conf_add_port(cd->conf, cd->pool, *p_port, NULL, &cd->call_slot);
}
static void on_stream_destroyed(pjsua_call_id call_id,
pjmedia_stream *strm,
unsigned stream_idx){
log_message("RUNNING... on_stream_destroyed\n");
struct call_data *cd;
cd = (struct call_data*) pjsua_call_get_user_data(call_id);
if (!cd)
return;
pjmedia_conf_remove_port(cd->conf, cd->call_slot);
}
/////////////////////////////////////////////////////////////////////////////////
// main()
/////////////////////////////////////////////////////////////////////////////////
//argv[1] may contain URL to call.
int main(int argc, char *argv[]){
pjsua_acc_id acc_id;
app_cfg.tts_file = "message.wav";
app_cfg.record_call = 1;
app_cfg.record_file = "recording.wav";
app_cfg.repetition_limit = 250;
app_cfg.silent_mode = 0;
pj_status_t status;
/* Create pjsua first! */
status = pjsua_create();
if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);
/* If argument is specified, it's got to be a valid SIP URL */
if (argc > 1) {
status = pjsua_verify_url(argv[1]);
if (status != PJ_SUCCESS) error_exit("Invalid URL in argv", status);
} else {
PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", status));
}
/* Init pjsua */
{
pjsua_config cfg;
pjsua_logging_config log_cfg;
pjsua_config_default(&cfg); //see pjsua.h
//cfg.cb.on_incoming_call = &on_incoming_call;
cfg.cb.on_call_media_state = &on_call_media_state;
cfg.cb.on_call_state = &on_call_state;
cfg.cb.on_stream_created = &on_stream_created;
cfg.cb.on_stream_destroyed = &on_stream_destroyed;
pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 4;
pjsua_media_config media_cfg;
pjsua_media_config_default(&media_cfg);
//media_cfg.snd_play_latency = 160; //default 2000
//media_cfg.snd_rec_latency = 160; //default 2000
//media_cfg.clock_rate = 16000;
//media_cfg.snd_clock_rate = 16000;
media_cfg.quality = 10;
media_cfg.ec_tail_len = 0;
//media_cfg.ptime = 100; //100 milliseconds //default seems to be 20?
status = pjsua_init(&cfg, &log_cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);
}
/* Add UDP transport. */
{
pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg);
cfg.port = 5060;
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
}
/* Initialization is done, now start pjsua */
status = pjsua_start();
if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);
// pjsua_set_null_snd_dev() now not used, because instead using pjsua_set_no_snd_dev() (see below)
// disable sound - use null sound device
//status = pjsua_set_null_snd_dev();
//if (status != PJ_SUCCESS) error_exit("Error disabling audio", status);
pjmedia_port* mainconfbridgeport;
mainconfbridgeport = pjsua_set_no_snd_dev();
/* Register to SIP server by creating SIP account. */
{
pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg);
cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
if (status != PJ_SUCCESS) error_exit("Error adding account", status);
}
/* If URL is specified, make call to the URL. */
if (argc > 1) {
pj_str_t uri = pj_str(argv[1]);
status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, NULL);
if (status != PJ_SUCCESS) error_exit("Error making call", status);
}
//Wait until user press "q" to quit.
for (;;) {
char option[10];
puts("Press 'h' to hangup all calls, 'q' to quit");
if (fgets(option, sizeof(option), stdin) == NULL) {
puts("EOF while reading stdin, will quit now..");
break;
}
if (option[0] == 'q')
break;
if (option[0] == 'h')
pjsua_call_hangup_all();
}
/* Destroy pjsua */
pjsua_destroy();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment