263 lines
6.8 KiB
C
263 lines
6.8 KiB
C
|
/*
|
||
|
* Copyright (C) 2021 Aon plc
|
||
|
*
|
||
|
* 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
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*/
|
||
|
|
||
|
#include <unistd.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <time.h>
|
||
|
#include <stdio.h>
|
||
|
#include <signal.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include <pjsua-lib/pjsua.h>
|
||
|
|
||
|
#define SIGNATURE PJMEDIA_SIG_CLASS_PORT_AUD('D','M')
|
||
|
|
||
|
struct dmodem {
|
||
|
pjmedia_port base;
|
||
|
pj_timestamp timestamp;
|
||
|
pj_sock_t sock;
|
||
|
};
|
||
|
|
||
|
static struct dmodem port;
|
||
|
static bool destroying = false;
|
||
|
static pj_pool_t *pool;
|
||
|
|
||
|
static void error_exit(const char *title, pj_status_t status) {
|
||
|
pjsua_perror(__FILE__, title, status);
|
||
|
if (!destroying) {
|
||
|
destroying = true;
|
||
|
pjsua_destroy();
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static pj_status_t dmodem_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) {
|
||
|
struct dmodem *sm = (struct dmodem *)this_port;
|
||
|
int len;
|
||
|
|
||
|
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
|
||
|
if ((len=write(sm->sock, frame->buf, frame->size)) != frame->size) {
|
||
|
error_exit("error writing frame",0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return PJ_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static pj_status_t dmodem_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) {
|
||
|
struct dmodem *sm = (struct dmodem *)this_port;
|
||
|
frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info); // MAX? what is
|
||
|
|
||
|
int len;
|
||
|
if ((len=read(sm->sock, frame->buf, frame->size)) != frame->size) {
|
||
|
error_exit("error reading frame",0);
|
||
|
}
|
||
|
|
||
|
frame->timestamp.u64 = sm->timestamp.u64;
|
||
|
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
||
|
sm->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
|
||
|
|
||
|
return PJ_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static pj_status_t dmodem_on_destroy(pjmedia_port *this_port) {
|
||
|
printf("destroy\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
/* Callback called by the library when call's state has changed */
|
||
|
static void on_call_state(pjsua_call_id call_id, pjsip_event *e) {
|
||
|
pjsua_call_info ci;
|
||
|
|
||
|
PJ_UNUSED_ARG(e);
|
||
|
|
||
|
pjsua_call_get_info(call_id, &ci);
|
||
|
PJ_LOG(3,(__FILE__, "Call %d state=%.*s", call_id,
|
||
|
(int)ci.state_text.slen,
|
||
|
ci.state_text.ptr));
|
||
|
|
||
|
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
|
||
|
close(port.sock);
|
||
|
if (!destroying) {
|
||
|
destroying = true;
|
||
|
pjsua_destroy();
|
||
|
exit(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Callback called by the library when call's media state has changed */
|
||
|
static void on_call_media_state(pjsua_call_id call_id) {
|
||
|
pjsua_call_info ci;
|
||
|
pjsua_conf_port_id port_id;
|
||
|
static int done=0;
|
||
|
|
||
|
pjsua_call_get_info(call_id, &ci);
|
||
|
|
||
|
// printf("media_status %d media_cnt %d ci.conf_slot %d aud.conf_slot %d\n",ci.media_status,ci.media_cnt,ci.conf_slot,ci.media[0].stream.aud.conf_slot);
|
||
|
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
|
||
|
if (!done) {
|
||
|
pjsua_conf_add_port(pool, &port.base, &port_id);
|
||
|
pjsua_conf_connect(ci.conf_slot, port_id);
|
||
|
pjsua_conf_connect(port_id, ci.conf_slot);
|
||
|
done = 1;
|
||
|
}
|
||
|
} else {
|
||
|
done = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int main(int argc, char *argv[]) {
|
||
|
pjsua_acc_id acc_id;
|
||
|
pj_status_t status;
|
||
|
|
||
|
if (argc != 3) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
signal(SIGPIPE,SIG_IGN);
|
||
|
|
||
|
char *dialstr = argv[1];
|
||
|
|
||
|
char *sip_user = getenv("SIP_LOGIN");
|
||
|
if (!sip_user) {
|
||
|
return -1;
|
||
|
}
|
||
|
char *sip_domain = strchr(sip_user,'@');
|
||
|
if (!sip_domain) {
|
||
|
return -1;
|
||
|
}
|
||
|
*sip_domain++ = '\0';
|
||
|
char *sip_pass = strchr(sip_user,':');
|
||
|
if (!sip_pass) {
|
||
|
return -1;
|
||
|
}
|
||
|
*sip_pass++ = '\0';
|
||
|
|
||
|
status = pjsua_create();
|
||
|
if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);
|
||
|
|
||
|
/* Init pjsua */
|
||
|
{
|
||
|
pjsua_config cfg;
|
||
|
pjsua_logging_config log_cfg;
|
||
|
pjsua_media_config med_cfg;
|
||
|
|
||
|
pjsua_config_default(&cfg);
|
||
|
cfg.cb.on_call_media_state = &on_call_media_state;
|
||
|
cfg.cb.on_call_state = &on_call_state;
|
||
|
|
||
|
pjsua_logging_config_default(&log_cfg);
|
||
|
log_cfg.console_level = 4;
|
||
|
|
||
|
pjsua_media_config_default(&med_cfg);
|
||
|
med_cfg.no_vad = true;
|
||
|
med_cfg.ec_tail_len = 0;
|
||
|
med_cfg.jb_max = 2000;
|
||
|
// med_cfg.jb_init = 200;
|
||
|
med_cfg.audio_frame_ptime = 5;
|
||
|
|
||
|
status = pjsua_init(&cfg, &log_cfg, &med_cfg);
|
||
|
if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);
|
||
|
}
|
||
|
|
||
|
pjsua_set_ec(0,0); // maybe?
|
||
|
pjsua_set_null_snd_dev();
|
||
|
|
||
|
/* g711 only */
|
||
|
pjsua_codec_info codecs[32];
|
||
|
unsigned count = sizeof(codecs)/sizeof(*codecs);
|
||
|
pjsua_enum_codecs(codecs,&count);
|
||
|
for (int i=0; i<count; i++) {
|
||
|
int pri = 0;
|
||
|
if (pj_strcmp2(&codecs[i].codec_id,"PCMU/8000/1") == 0) {
|
||
|
pri = 1;
|
||
|
} else if (pj_strcmp2(&codecs[i].codec_id,"PCMA/8000/1") == 0) {
|
||
|
pri = 2;
|
||
|
}
|
||
|
pjsua_codec_set_priority(&codecs[i].codec_id, pri);
|
||
|
// printf("codec: %s %d\n",pj_strbuf(&codecs[i].codec_id),pri);
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
}
|
||
|
|
||
|
pj_caching_pool cp;
|
||
|
pj_caching_pool_init(&cp, NULL, 1024*1024);
|
||
|
pool = pj_pool_create(&cp.factory, "pool1", 4000, 4000, NULL);
|
||
|
|
||
|
pj_str_t name = pj_str("dmodem");
|
||
|
|
||
|
memset(&port,0,sizeof(port));
|
||
|
port.sock = atoi(argv[2]); // inherited from parent
|
||
|
pjmedia_port_info_init(&port.base.info, &name, SIGNATURE, 9600, 1, 16, 192);
|
||
|
port.base.put_frame = dmodem_put_frame;
|
||
|
port.base.get_frame = dmodem_get_frame;
|
||
|
port.base.on_destroy = dmodem_on_destroy;
|
||
|
|
||
|
char buf[384];
|
||
|
memset(buf,0,sizeof(buf));
|
||
|
write(port.sock, buf, sizeof(buf));
|
||
|
|
||
|
/* Initialization is done, now start pjsua */
|
||
|
status = pjsua_start();
|
||
|
if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);
|
||
|
|
||
|
{
|
||
|
pjsua_acc_config cfg;
|
||
|
pjsua_acc_config_default(&cfg);
|
||
|
snprintf(buf,sizeof(buf),"sip:%s@%s",sip_user,sip_domain);
|
||
|
pj_strdup2(pool,&cfg.id,buf);
|
||
|
snprintf(buf,sizeof(buf),"sip:%s",sip_domain);
|
||
|
pj_strdup2(pool,&cfg.reg_uri,buf);
|
||
|
cfg.register_on_acc_add = false;
|
||
|
cfg.cred_count = 1;
|
||
|
cfg.cred_info[0].realm = pj_str("*");
|
||
|
cfg.cred_info[0].scheme = pj_str("digest");
|
||
|
cfg.cred_info[0].username = pj_str(sip_user);
|
||
|
cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
|
||
|
cfg.cred_info[0].data = pj_str(sip_pass);
|
||
|
|
||
|
status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
|
||
|
if (status != PJ_SUCCESS) error_exit("Error adding account", status);
|
||
|
}
|
||
|
|
||
|
snprintf(buf,sizeof(buf),"sip:%s@%s",dialstr,sip_domain);
|
||
|
printf("calling %s\n",buf);
|
||
|
pj_str_t uri = pj_str(buf);
|
||
|
|
||
|
pjsua_call_id callid;
|
||
|
status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, &callid);
|
||
|
if (status != PJ_SUCCESS) error_exit("Error making call", status);
|
||
|
|
||
|
struct timespec ts = {100, 0};
|
||
|
while(1) {
|
||
|
nanosleep(&ts,NULL);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|