2006-03-31 19:38:09 +00:00
|
|
|
/*
|
|
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
|
|
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
|
|
|
*
|
|
|
|
* Version: MPL 1.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Anthony Minessale II <anthmct@yahoo.com>
|
|
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Anthony Minessale II <anthmct@yahoo.com>
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* mod_dingaling.c -- Jingle Endpoint Module
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <switch.h>
|
|
|
|
#include <libdingaling.h>
|
|
|
|
|
2006-04-13 23:19:33 +00:00
|
|
|
#define DL_CAND_WAIT 10000000
|
2006-04-15 05:56:23 +00:00
|
|
|
#define DL_CAND_INITIAL_WAIT 2000000
|
2006-04-13 23:19:33 +00:00
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
static const char modname[] = "mod_dingaling";
|
|
|
|
|
|
|
|
static switch_memory_pool *module_pool = NULL;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
TFLAG_IO = (1 << 0),
|
|
|
|
TFLAG_INBOUND = (1 << 1),
|
|
|
|
TFLAG_OUTBOUND = (1 << 2),
|
|
|
|
TFLAG_READING = (1 << 3),
|
|
|
|
TFLAG_WRITING = (1 << 4),
|
|
|
|
TFLAG_BYE = (1 << 5),
|
|
|
|
TFLAG_VOICE = (1 << 6),
|
|
|
|
TFLAG_RTP_READY = (1 << 7),
|
|
|
|
TFLAG_CODEC_READY = (1 << 8),
|
2006-04-15 22:30:25 +00:00
|
|
|
TFLAG_TRANSPORT = (1 << 9),
|
|
|
|
TFLAG_ANSWER = (1 << 10),
|
2006-03-31 19:38:09 +00:00
|
|
|
} TFLAGS;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
GFLAG_MY_CODEC_PREFS = (1 << 0)
|
|
|
|
} GFLAGS;
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
int debug;
|
|
|
|
char *dialplan;
|
|
|
|
char *codec_string;
|
|
|
|
char *codec_order[SWITCH_MAX_CODECS];
|
|
|
|
int codec_order_last;
|
|
|
|
char *codec_rates_string;
|
|
|
|
char *codec_rates[SWITCH_MAX_CODECS];
|
|
|
|
int codec_rates_last;
|
|
|
|
unsigned int flags;
|
|
|
|
unsigned int init;
|
|
|
|
switch_hash *profile_hash;
|
|
|
|
int running;
|
|
|
|
int handles;
|
|
|
|
} globals;
|
|
|
|
|
|
|
|
struct mdl_profile {
|
|
|
|
char *name;
|
|
|
|
char *login;
|
|
|
|
char *password;
|
|
|
|
char *message;
|
|
|
|
char *dialplan;
|
|
|
|
char *ip;
|
|
|
|
char *extip;
|
|
|
|
char *lanaddr;
|
|
|
|
char *exten;
|
|
|
|
unsigned int flags;
|
|
|
|
ldl_handle_t *handle;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct private_object {
|
|
|
|
unsigned int flags;
|
|
|
|
switch_codec read_codec;
|
|
|
|
switch_codec write_codec;
|
|
|
|
struct switch_frame read_frame;
|
|
|
|
struct mdl_profile *profile;
|
|
|
|
switch_core_session *session;
|
|
|
|
switch_caller_profile *caller_profile;
|
|
|
|
unsigned short samprate;
|
|
|
|
switch_mutex_t *mutex;
|
|
|
|
switch_codec_interface *codecs[SWITCH_MAX_CODECS];
|
|
|
|
unsigned int num_codecs;
|
|
|
|
int codec_index;
|
2006-04-03 21:00:13 +00:00
|
|
|
struct switch_rtp *rtp_session;
|
2006-03-31 19:38:09 +00:00
|
|
|
ldl_session_t *dlsession;
|
|
|
|
char *remote_ip;
|
2006-04-05 20:17:22 +00:00
|
|
|
switch_port_t local_port;
|
|
|
|
switch_port_t remote_port;
|
2006-04-06 23:31:01 +00:00
|
|
|
char local_user[17];
|
2006-04-15 22:30:25 +00:00
|
|
|
char local_pass[17];
|
2006-03-31 19:38:09 +00:00
|
|
|
char *remote_user;
|
|
|
|
unsigned int cand_id;
|
|
|
|
unsigned int desc_id;
|
|
|
|
char last_digit;
|
|
|
|
unsigned int dc;
|
|
|
|
time_t last_digit_time;
|
|
|
|
switch_queue_t *dtmf_queue;
|
|
|
|
char out_digit;
|
|
|
|
unsigned char out_digit_packet[4];
|
|
|
|
unsigned int out_digit_sofar;
|
|
|
|
unsigned int out_digit_dur;
|
2006-04-04 04:15:39 +00:00
|
|
|
uint16_t out_digit_seq;
|
2006-03-31 19:38:09 +00:00
|
|
|
int32_t timestamp_send;
|
|
|
|
int32_t timestamp_recv;
|
|
|
|
int32_t timestamp_dtmf;
|
2006-04-13 16:19:49 +00:00
|
|
|
uint32_t last_read;
|
2006-03-31 19:38:09 +00:00
|
|
|
char *codec_name;
|
2006-04-18 16:50:34 +00:00
|
|
|
switch_payload_t codec_num;
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_time_t next_desc;
|
2006-04-15 16:02:31 +00:00
|
|
|
switch_time_t next_cand;
|
2006-04-13 17:15:51 +00:00
|
|
|
char *stun_ip;
|
2006-04-14 05:53:59 +00:00
|
|
|
uint16_t stun_port;
|
2006-03-31 19:38:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct rfc2833_digit {
|
|
|
|
char digit;
|
|
|
|
int duration;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan)
|
|
|
|
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string)
|
|
|
|
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_rates_string, globals.codec_rates_string)
|
|
|
|
|
|
|
|
|
|
|
|
static switch_status channel_on_init(switch_core_session *session);
|
|
|
|
static switch_status channel_on_hangup(switch_core_session *session);
|
|
|
|
static switch_status channel_on_ring(switch_core_session *session);
|
|
|
|
static switch_status channel_on_loopback(switch_core_session *session);
|
|
|
|
static switch_status channel_on_transmit(switch_core_session *session);
|
|
|
|
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile,
|
|
|
|
switch_core_session **new_session, switch_memory_pool *pool);
|
|
|
|
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout,
|
|
|
|
switch_io_flag flags, int stream_id);
|
|
|
|
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout,
|
|
|
|
switch_io_flag flags, int stream_id);
|
|
|
|
static switch_status channel_kill_channel(switch_core_session *session, int sig);
|
|
|
|
static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsession, ldl_signal_t signal, char *msg);
|
|
|
|
static ldl_status handle_response(ldl_handle_t *handle, char *id);
|
|
|
|
static switch_status load_config(void);
|
|
|
|
|
2006-04-14 02:05:29 +00:00
|
|
|
static void dl_logger(char *file, const char *func, int line, int level, char *fmt, ...)
|
|
|
|
{
|
2006-04-14 03:04:53 +00:00
|
|
|
va_list ap;
|
2006-04-14 03:12:55 +00:00
|
|
|
char data[1024];
|
2006-04-14 03:04:53 +00:00
|
|
|
|
2006-04-14 03:12:55 +00:00
|
|
|
va_start(ap, fmt);
|
|
|
|
|
|
|
|
vsnprintf(data, sizeof(data), fmt, ap);
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, SWITCH_LOG_DEBUG, data);
|
2006-04-14 03:04:53 +00:00
|
|
|
|
|
|
|
va_end(ap);
|
2006-04-14 02:05:29 +00:00
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
static void get_codecs(struct private_object *tech_pvt)
|
|
|
|
{
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
assert(tech_pvt->session != NULL);
|
|
|
|
|
|
|
|
if (globals.codec_string) {
|
|
|
|
if ((tech_pvt->num_codecs = switch_loadable_module_get_codecs_sorted(tech_pvt->codecs,
|
|
|
|
SWITCH_MAX_CODECS,
|
|
|
|
globals.codec_order,
|
|
|
|
globals.codec_order_last)) <= 0) {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO codecs?\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (((tech_pvt->num_codecs =
|
|
|
|
switch_loadable_module_get_codecs(switch_core_session_get_pool(tech_pvt->session), tech_pvt->codecs, SWITCH_MAX_CODECS))) <= 0) {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO codecs?\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void *SWITCH_THREAD_FUNC handle_thread_run(switch_thread *thread, void *obj)
|
|
|
|
{
|
|
|
|
ldl_handle_t *handle = obj;
|
|
|
|
struct mdl_profile *profile = NULL;
|
|
|
|
|
|
|
|
profile = ldl_handle_get_private(handle);
|
|
|
|
globals.handles++;
|
|
|
|
ldl_handle_run(handle);
|
|
|
|
globals.handles--;
|
|
|
|
ldl_handle_destroy(&handle);
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Handle %s [%s] Destroyed\n", profile->name, profile->login);
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_thread_launch(ldl_handle_t *handle)
|
|
|
|
{
|
|
|
|
switch_thread *thread;
|
|
|
|
switch_threadattr_t *thd_attr = NULL;
|
|
|
|
|
|
|
|
switch_threadattr_create(&thd_attr, module_pool);
|
|
|
|
switch_threadattr_detach_set(thd_attr, 1);
|
|
|
|
switch_thread_create(&thread, thd_attr, handle_thread_run, handle, module_pool);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
static int activate_rtp(struct private_object *tech_pvt)
|
2006-03-31 19:38:09 +00:00
|
|
|
{
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_channel *channel = switch_core_session_get_channel(tech_pvt->session);
|
|
|
|
const char *err;
|
2006-04-19 19:44:05 +00:00
|
|
|
int ms = 20;
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
if (tech_pvt->rtp_session) {
|
|
|
|
return 0;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-19 19:44:05 +00:00
|
|
|
if (!strcasecmp(tech_pvt->codec_name, "ilbc")) {
|
|
|
|
ms = 30;
|
|
|
|
}
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
if (switch_core_codec_init(&tech_pvt->read_codec,
|
|
|
|
tech_pvt->codec_name,
|
|
|
|
8000,
|
2006-04-19 19:44:05 +00:00
|
|
|
ms,
|
2006-04-15 22:30:25 +00:00
|
|
|
1,
|
|
|
|
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
|
|
|
|
NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't load codec?\n");
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
tech_pvt->read_frame.rate = tech_pvt->read_codec.implementation->samples_per_second;
|
|
|
|
tech_pvt->read_frame.codec = &tech_pvt->read_codec;
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set Read Codec to %s\n", tech_pvt->codec_name);
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
if (switch_core_codec_init(&tech_pvt->write_codec,
|
|
|
|
tech_pvt->codec_name,
|
|
|
|
8000,
|
2006-04-19 19:44:05 +00:00
|
|
|
ms,
|
2006-04-15 22:30:25 +00:00
|
|
|
1,
|
|
|
|
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
|
|
|
|
NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't load codec?\n");
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set Write Codec to %s\n", tech_pvt->codec_name);
|
|
|
|
|
|
|
|
switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec);
|
|
|
|
switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec);
|
|
|
|
|
2006-04-15 05:56:23 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SETUP RTP %s:%d -> %s:%d\n", tech_pvt->profile->ip, tech_pvt->local_port, tech_pvt->remote_ip, tech_pvt->remote_port);
|
|
|
|
|
|
|
|
if (!(tech_pvt->rtp_session = switch_rtp_new(tech_pvt->profile->ip,
|
|
|
|
tech_pvt->local_port,
|
|
|
|
tech_pvt->remote_ip,
|
|
|
|
tech_pvt->remote_port,
|
|
|
|
tech_pvt->codec_num,
|
|
|
|
tech_pvt->read_codec.implementation->encoded_bytes_per_frame,
|
|
|
|
tech_pvt->read_codec.implementation->microseconds_per_frame,
|
2006-04-17 18:25:43 +00:00
|
|
|
SWITCH_RTP_FLAG_USE_TIMER | SWITCH_RTP_FLAG_AUTOADJ,
|
2006-04-15 22:30:25 +00:00
|
|
|
NULL,
|
|
|
|
&err, switch_core_session_get_pool(tech_pvt->session)))) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP ERROR %s\n", err);
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
return -1;
|
2006-04-13 17:15:51 +00:00
|
|
|
}
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_rtp_activate_ice(tech_pvt->rtp_session, tech_pvt->remote_user, tech_pvt->local_user);
|
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
static int do_candidates(struct private_object *tech_pvt, int force)
|
|
|
|
{
|
|
|
|
switch_channel *channel = switch_core_session_get_channel(tech_pvt->session);
|
|
|
|
assert(channel != NULL);
|
2006-04-15 16:02:31 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
tech_pvt->next_cand += DL_CAND_WAIT;
|
2006-04-15 16:02:31 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
if (force || !switch_test_flag(tech_pvt, TFLAG_RTP_READY)) {
|
2006-03-31 19:38:09 +00:00
|
|
|
ldl_candidate_t cand[1];
|
|
|
|
char *advip = tech_pvt->profile->extip ? tech_pvt->profile->extip : tech_pvt->profile->ip;
|
2006-04-05 22:46:50 +00:00
|
|
|
char *err;
|
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
memset(cand, 0, sizeof(cand));
|
2006-04-04 16:07:40 +00:00
|
|
|
switch_stun_random_string(tech_pvt->local_user, 16, NULL);
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_stun_random_string(tech_pvt->local_pass, 16, NULL);
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-05 22:46:50 +00:00
|
|
|
|
2006-04-05 20:17:22 +00:00
|
|
|
cand[0].port = tech_pvt->local_port;
|
2006-04-05 22:46:50 +00:00
|
|
|
cand[0].address = advip;
|
|
|
|
|
|
|
|
if (!strncasecmp(advip, "stun:", 5)) {
|
2006-04-07 01:27:45 +00:00
|
|
|
char *stun_ip = advip + 5;
|
2006-04-13 17:15:51 +00:00
|
|
|
|
|
|
|
if (tech_pvt->stun_ip) {
|
|
|
|
cand[0].address = tech_pvt->stun_ip;
|
|
|
|
cand[0].port = tech_pvt->stun_port;
|
|
|
|
} else {
|
|
|
|
if (!stun_ip) {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! NO STUN SERVER!\n");
|
2006-04-13 17:15:51 +00:00
|
|
|
switch_channel_hangup(channel);
|
2006-04-15 22:30:25 +00:00
|
|
|
return -1;
|
2006-04-13 17:15:51 +00:00
|
|
|
}
|
2006-04-07 01:27:45 +00:00
|
|
|
|
2006-04-13 17:15:51 +00:00
|
|
|
cand[0].address = tech_pvt->profile->ip;
|
2006-04-15 16:02:31 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Stun Lookup Local %s:%d\n", cand[0].address, cand[0].port);
|
2006-04-13 17:15:51 +00:00
|
|
|
if (switch_stun_lookup(&cand[0].address,
|
|
|
|
&cand[0].port,
|
|
|
|
stun_ip,
|
|
|
|
SWITCH_STUN_DEFAULT_PORT,
|
|
|
|
&err,
|
|
|
|
switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! %s:%d [%s]\n", stun_ip, SWITCH_STUN_DEFAULT_PORT, err);
|
2006-04-13 17:15:51 +00:00
|
|
|
switch_channel_hangup(channel);
|
2006-04-15 22:30:25 +00:00
|
|
|
return -1;
|
2006-04-13 17:15:51 +00:00
|
|
|
}
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Stun Success %s:%d\n", cand[0].address, cand[0].port);
|
2006-04-05 22:46:50 +00:00
|
|
|
}
|
|
|
|
cand[0].type = "stun";
|
2006-04-13 17:15:51 +00:00
|
|
|
tech_pvt->stun_ip = switch_core_session_strdup(tech_pvt->session, cand[0].address);
|
|
|
|
tech_pvt->stun_port = cand[0].port;
|
2006-04-05 22:46:50 +00:00
|
|
|
} else {
|
|
|
|
cand[0].type = "local";
|
|
|
|
}
|
|
|
|
|
|
|
|
cand[0].name = "rtp";
|
2006-03-31 19:38:09 +00:00
|
|
|
cand[0].username = tech_pvt->local_user;
|
2006-04-15 22:30:25 +00:00
|
|
|
cand[0].password = tech_pvt->local_pass;
|
2006-03-31 19:38:09 +00:00
|
|
|
cand[0].pref = 1;
|
|
|
|
cand[0].protocol = "udp";
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Send Candidate %s:%d [%s]\n", cand[0].address, cand[0].port, cand[0].username);
|
2006-03-31 19:38:09 +00:00
|
|
|
tech_pvt->cand_id = ldl_session_candidates(tech_pvt->dlsession, cand, 1);
|
2006-04-15 05:00:51 +00:00
|
|
|
switch_set_flag(tech_pvt, TFLAG_RTP_READY);
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
2006-04-15 22:30:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_describe(struct private_object *tech_pvt, int force)
|
|
|
|
{
|
|
|
|
ldl_payload_t payloads[5];
|
|
|
|
switch_channel *channel = switch_core_session_get_channel(tech_pvt->session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt->next_desc += DL_CAND_WAIT;
|
|
|
|
|
|
|
|
memset(payloads, 0, sizeof(payloads));
|
|
|
|
|
|
|
|
if (!tech_pvt->num_codecs) {
|
|
|
|
get_codecs(tech_pvt);
|
|
|
|
if (!tech_pvt->num_codecs) {
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_channel_hangup(channel);
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_BYE);
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_IO);
|
2006-04-15 22:30:25 +00:00
|
|
|
return -1;
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
if (force || !switch_test_flag(tech_pvt, TFLAG_CODEC_READY)) {
|
|
|
|
if (tech_pvt->codec_index < 0) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Don't have my codec yet here's one\n");
|
|
|
|
tech_pvt->codec_name = tech_pvt->codecs[0]->iananame;
|
|
|
|
tech_pvt->codec_num = tech_pvt->codecs[0]->ianacode;
|
|
|
|
tech_pvt->codec_index = 0;
|
|
|
|
|
|
|
|
payloads[0].name = tech_pvt->codecs[0]->iananame;
|
|
|
|
payloads[0].id = tech_pvt->codecs[0]->ianacode;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
payloads[0].name = tech_pvt->codecs[tech_pvt->codec_index]->iananame;
|
|
|
|
payloads[0].id = tech_pvt->codecs[tech_pvt->codec_index]->ianacode;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Send Describe [%s]\n", payloads[0].name);
|
|
|
|
tech_pvt->desc_id = ldl_session_describe(tech_pvt->dlsession, payloads, 1,
|
|
|
|
switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? LDL_DESCRIPTION_INITIATE : LDL_DESCRIPTION_ACCEPT);
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_CODEC_READY);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
static void *SWITCH_THREAD_FUNC negotiate_thread_run(switch_thread *thread, void *obj)
|
|
|
|
{
|
|
|
|
switch_core_session *session = obj;
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_channel *channel;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
switch_time_t started;
|
|
|
|
switch_time_t now;
|
|
|
|
unsigned int elapsed;
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_IO);
|
|
|
|
|
|
|
|
started = switch_time_now();
|
|
|
|
|
|
|
|
if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
|
|
|
tech_pvt->next_desc = switch_time_now();
|
|
|
|
} else {
|
|
|
|
tech_pvt->next_cand = switch_time_now() + DL_CAND_WAIT;
|
|
|
|
tech_pvt->next_desc = switch_time_now() + DL_CAND_WAIT;
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
while(! (switch_test_flag(tech_pvt, TFLAG_CODEC_READY) &&
|
|
|
|
switch_test_flag(tech_pvt, TFLAG_RTP_READY) &&
|
|
|
|
switch_test_flag(tech_pvt, TFLAG_ANSWER) &&
|
|
|
|
switch_test_flag(tech_pvt, TFLAG_TRANSPORT))) {
|
|
|
|
now = switch_time_now();
|
|
|
|
elapsed = (unsigned int)((now - started) / 1000);
|
|
|
|
|
|
|
|
if (switch_channel_get_state(channel) >= CS_HANGUP || switch_test_flag(tech_pvt, TFLAG_BYE)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (now >= tech_pvt->next_desc) {
|
|
|
|
if (do_describe(tech_pvt, 0) < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tech_pvt->next_cand && now >= tech_pvt->next_cand) {
|
|
|
|
if (do_candidates(tech_pvt, 0) < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (elapsed > 60000) {
|
2006-04-12 20:48:47 +00:00
|
|
|
switch_channel_hangup(channel);
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_set_flag(tech_pvt, TFLAG_BYE);
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_IO);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (switch_test_flag(tech_pvt, TFLAG_BYE) || ! switch_test_flag(tech_pvt, TFLAG_IO)) {
|
2006-04-12 20:48:47 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_yield(1000);
|
|
|
|
//printf("WAIT %s %d %d %d %d\n", switch_channel_get_name(channel), switch_test_flag(tech_pvt, TFLAG_TRANSPORT), switch_test_flag(tech_pvt, TFLAG_CODEC_READY), switch_test_flag(tech_pvt, TFLAG_RTP_READY), switch_test_flag(tech_pvt, TFLAG_ANSWER));
|
2006-04-12 20:48:47 +00:00
|
|
|
}
|
2006-04-15 22:30:25 +00:00
|
|
|
|
|
|
|
if (switch_channel_get_state(channel) >= CS_HANGUP || switch_test_flag(tech_pvt, TFLAG_BYE)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-04-12 20:48:47 +00:00
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
activate_rtp(tech_pvt);
|
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
2006-04-15 22:30:25 +00:00
|
|
|
do_candidates(tech_pvt, 0);
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_channel_answer(channel);
|
|
|
|
//printf("***************************ANSWER\n");
|
|
|
|
} else {
|
|
|
|
switch_core_session_thread_launch(session);
|
|
|
|
}
|
|
|
|
switch_channel_set_state(channel, CS_INIT);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void negotiate_thread_launch(switch_core_session *session)
|
|
|
|
{
|
|
|
|
switch_thread *thread;
|
|
|
|
switch_threadattr_t *thd_attr = NULL;
|
|
|
|
|
|
|
|
switch_threadattr_create(&thd_attr, switch_core_session_get_pool(session));
|
|
|
|
switch_threadattr_detach_set(thd_attr, 1);
|
|
|
|
switch_thread_create(&thread, thd_attr, negotiate_thread_run, session, switch_core_session_get_pool(session));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
State methods they get called when the state changes to the specific state
|
|
|
|
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
|
|
|
|
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
|
|
|
|
*/
|
|
|
|
static switch_status channel_on_init(switch_core_session *session)
|
|
|
|
{
|
|
|
|
switch_channel *channel;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
2006-04-06 19:50:53 +00:00
|
|
|
tech_pvt->read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
|
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
/* Move Channel's State Machine to RING */
|
|
|
|
switch_channel_set_state(channel, CS_RING);
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_on_ring(switch_core_session *session)
|
|
|
|
{
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL RING\n", switch_channel_get_name(channel));
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_on_execute(switch_core_session *session)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_on_hangup(switch_core_session *session)
|
|
|
|
{
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_IO);
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_VOICE);
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_BYE);
|
|
|
|
|
|
|
|
if (tech_pvt->dlsession) {
|
|
|
|
ldl_session_terminate(tech_pvt->dlsession);
|
|
|
|
ldl_session_destroy(&tech_pvt->dlsession);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tech_pvt->rtp_session) {
|
2006-04-03 21:00:13 +00:00
|
|
|
switch_rtp_destroy(&tech_pvt->rtp_session);
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NUKE RTP\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
tech_pvt->rtp_session = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tech_pvt->read_codec.implementation) {
|
|
|
|
switch_core_codec_destroy(&tech_pvt->read_codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tech_pvt->write_codec.implementation) {
|
|
|
|
switch_core_codec_destroy(&tech_pvt->write_codec);
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_kill_channel(switch_core_session *session, int sig)
|
|
|
|
{
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
if ((channel = switch_core_session_get_channel(session))) {
|
|
|
|
if ((tech_pvt = switch_core_session_get_private(session))) {
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_IO);
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_VOICE);
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_BYE);
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
if (tech_pvt->dlsession) {
|
|
|
|
ldl_session_terminate(tech_pvt->dlsession);
|
|
|
|
}
|
2006-03-31 23:23:10 +00:00
|
|
|
if (tech_pvt->rtp_session) {
|
2006-04-06 02:47:11 +00:00
|
|
|
switch_rtp_kill_socket(tech_pvt->rtp_session);
|
2006-03-31 23:23:10 +00:00
|
|
|
}
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL KILL\n", switch_channel_get_name(channel));
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_on_loopback(switch_core_session *session)
|
|
|
|
{
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL LOOPBACK\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_on_transmit(switch_core_session *session)
|
|
|
|
{
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL TRANSMIT\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_waitfor_read(switch_core_session *session, int ms, int stream_id)
|
|
|
|
{
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_waitfor_write(switch_core_session *session, int ms, int stream_id)
|
|
|
|
{
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_send_dtmf(switch_core_session *session, char *dtmf)
|
|
|
|
{
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
//char *digit;
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout,
|
|
|
|
switch_io_flag flags, int stream_id)
|
|
|
|
{
|
|
|
|
struct private_object *tech_pvt = NULL;
|
2006-04-09 00:10:13 +00:00
|
|
|
uint32_t bytes = 0;
|
|
|
|
switch_size_t samples = 0, frames = 0, ms = 0;
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_channel *channel = NULL;
|
2006-04-18 16:50:34 +00:00
|
|
|
switch_payload_t payload = 0;
|
2006-04-18 16:40:18 +00:00
|
|
|
switch_status status;
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
|
2006-04-13 00:47:16 +00:00
|
|
|
if (!tech_pvt->rtp_session) {
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
tech_pvt->read_frame.datalen = 0;
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_READING);
|
|
|
|
|
|
|
|
bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
|
|
|
|
samples = tech_pvt->read_codec.implementation->samples_per_frame;
|
|
|
|
ms = tech_pvt->read_codec.implementation->microseconds_per_frame;
|
|
|
|
tech_pvt->read_frame.datalen = 0;
|
|
|
|
|
2006-04-06 19:50:53 +00:00
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
while (!switch_test_flag(tech_pvt, TFLAG_BYE) && switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
|
2006-04-06 19:50:53 +00:00
|
|
|
tech_pvt->read_frame.flags = 0;
|
2006-04-18 16:40:18 +00:00
|
|
|
status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame);
|
2006-04-10 18:28:46 +00:00
|
|
|
|
2006-04-18 16:40:18 +00:00
|
|
|
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
|
2006-04-10 18:28:46 +00:00
|
|
|
return SWITCH_STATUS_FALSE;
|
2006-04-18 16:40:18 +00:00
|
|
|
}
|
|
|
|
payload = tech_pvt->read_frame.payload;
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
/* RFC2833 ... TBD try harder to honor the duration etc.*/
|
|
|
|
if (payload == 101) {
|
|
|
|
unsigned char *packet = tech_pvt->read_frame.data;
|
|
|
|
int end = packet[1]&0x80;
|
|
|
|
int duration = (packet[2]<<8) + packet[3];
|
|
|
|
char key = switch_rfc2833_to_char(packet[0]);
|
|
|
|
|
|
|
|
/* SHEESH.... Curse you RFC2833 inventors!!!!*/
|
|
|
|
if ((time(NULL) - tech_pvt->last_digit_time) > 2) {
|
|
|
|
tech_pvt->last_digit = 0;
|
|
|
|
tech_pvt->dc = 0;
|
|
|
|
}
|
|
|
|
if (duration && end) {
|
|
|
|
if (key != tech_pvt->last_digit) {
|
|
|
|
char digit_str[] = {key, 0};
|
|
|
|
time(&tech_pvt->last_digit_time);
|
|
|
|
switch_channel_queue_dtmf(channel, digit_str);
|
|
|
|
}
|
|
|
|
if (++tech_pvt->dc >= 3) {
|
|
|
|
tech_pvt->last_digit = 0;
|
|
|
|
tech_pvt->dc = 0;
|
|
|
|
} else {
|
|
|
|
tech_pvt->last_digit = key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-13 16:19:49 +00:00
|
|
|
if (switch_test_flag(&tech_pvt->read_frame, SFF_CNG)) {
|
2006-04-13 23:19:33 +00:00
|
|
|
tech_pvt->read_frame.datalen = tech_pvt->last_read ? tech_pvt->last_read : tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
|
2006-04-13 16:19:49 +00:00
|
|
|
}
|
2006-04-13 23:19:33 +00:00
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
if (tech_pvt->read_frame.datalen > 0) {
|
|
|
|
bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
|
|
|
|
frames = (tech_pvt->read_frame.datalen / bytes);
|
|
|
|
samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
|
|
|
|
ms = frames * tech_pvt->read_codec.implementation->microseconds_per_frame;
|
|
|
|
tech_pvt->timestamp_recv += (int32_t) samples;
|
|
|
|
tech_pvt->read_frame.samples = (int) samples;
|
2006-04-13 16:19:49 +00:00
|
|
|
tech_pvt->last_read = tech_pvt->read_frame.datalen;
|
2006-04-06 19:50:53 +00:00
|
|
|
//printf("READ bytes=%d payload=%d frames=%d samples=%d ms=%d ts=%d sampcount=%d\n", (int)tech_pvt->read_frame.datalen, (int)payload, (int)frames, (int)samples, (int)ms, (int)tech_pvt->timestamp_recv, (int)tech_pvt->read_frame.samples);
|
2006-03-31 19:38:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch_yield(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_READING);
|
|
|
|
|
|
|
|
if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*frame = &tech_pvt->read_frame;
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout,
|
|
|
|
switch_io_flag flags, int stream_id)
|
|
|
|
{
|
|
|
|
struct private_object *tech_pvt;
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
switch_status status = SWITCH_STATUS_SUCCESS;
|
|
|
|
int bytes = 0, samples = 0, frames = 0;
|
|
|
|
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
2006-04-13 00:47:16 +00:00
|
|
|
if (!tech_pvt->rtp_session) {
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
if (!switch_test_flag(tech_pvt, TFLAG_RTP_READY)) {
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_WRITING);
|
|
|
|
|
|
|
|
|
|
|
|
bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
|
|
|
|
frames = ((int) frame->datalen / bytes);
|
|
|
|
samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
|
|
|
|
|
|
|
|
if (tech_pvt->out_digit_dur > 0) {
|
|
|
|
int x, ts, loops = 1, duration;
|
|
|
|
|
|
|
|
tech_pvt->out_digit_sofar += samples;
|
|
|
|
|
|
|
|
if (tech_pvt->out_digit_sofar >= tech_pvt->out_digit_dur) {
|
|
|
|
duration = tech_pvt->out_digit_dur;
|
|
|
|
tech_pvt->out_digit_packet[1] |= 0x80;
|
|
|
|
tech_pvt->out_digit_dur = 0;
|
|
|
|
loops = 3;
|
|
|
|
} else {
|
|
|
|
duration = tech_pvt->out_digit_sofar;
|
|
|
|
}
|
|
|
|
|
|
|
|
ts = tech_pvt->timestamp_dtmf += samples;
|
|
|
|
tech_pvt->out_digit_packet[2] = (unsigned char) (duration >> 8);
|
|
|
|
tech_pvt->out_digit_packet[3] = (unsigned char) duration;
|
|
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < loops; x++) {
|
2006-04-19 16:44:01 +00:00
|
|
|
switch_rtp_write_manual(tech_pvt->rtp_session, tech_pvt->out_digit_packet, 4, 0, 101, ts, tech_pvt->out_digit_seq, &frame->flags);
|
2006-04-03 21:00:13 +00:00
|
|
|
/*
|
2006-03-31 19:38:09 +00:00
|
|
|
printf("Send %s packet for [%c] ts=%d sofar=%u dur=%d\n", loops == 1 ? "middle" : "end", tech_pvt->out_digit, ts,
|
|
|
|
tech_pvt->out_digit_sofar, duration);
|
2006-04-03 21:00:13 +00:00
|
|
|
*/
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tech_pvt->out_digit_dur && tech_pvt->dtmf_queue && switch_queue_size(tech_pvt->dtmf_queue)) {
|
|
|
|
void *pop;
|
|
|
|
|
|
|
|
if (switch_queue_trypop(tech_pvt->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
|
|
|
|
int x, ts;
|
|
|
|
struct rfc2833_digit *rdigit = pop;
|
|
|
|
|
|
|
|
memset(tech_pvt->out_digit_packet, 0, 4);
|
|
|
|
tech_pvt->out_digit_sofar = 0;
|
|
|
|
tech_pvt->out_digit_dur = rdigit->duration;
|
|
|
|
tech_pvt->out_digit = rdigit->digit;
|
|
|
|
tech_pvt->out_digit_packet[0] = (unsigned char)switch_char_to_rfc2833(rdigit->digit);
|
|
|
|
tech_pvt->out_digit_packet[1] = 7;
|
|
|
|
|
|
|
|
ts = tech_pvt->timestamp_dtmf += samples;
|
|
|
|
tech_pvt->out_digit_seq++;
|
|
|
|
for (x = 0; x < 3; x++) {
|
2006-04-19 16:44:01 +00:00
|
|
|
switch_rtp_write_manual(tech_pvt->rtp_session, tech_pvt->out_digit_packet, 4, 1, 101, ts, tech_pvt->out_digit_seq, &frame->flags);
|
2006-04-03 21:00:13 +00:00
|
|
|
/*
|
2006-03-31 19:38:09 +00:00
|
|
|
printf("Send start packet for [%c] ts=%d sofar=%u dur=%d\n", tech_pvt->out_digit, ts,
|
|
|
|
tech_pvt->out_digit_sofar, 0);
|
2006-04-03 21:00:13 +00:00
|
|
|
*/
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(rdigit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-04-03 21:00:13 +00:00
|
|
|
//printf("%s send %d bytes %d samples in %d frames ts=%d\n", switch_channel_get_name(channel), frame->datalen, samples, frames, tech_pvt->timestamp_send);
|
2006-03-31 19:38:09 +00:00
|
|
|
|
2006-04-18 16:40:18 +00:00
|
|
|
if (switch_rtp_write_frame(tech_pvt->rtp_session, frame, samples) < 0) {
|
2006-04-15 06:06:46 +00:00
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
tech_pvt->timestamp_send += (int) samples;
|
|
|
|
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_WRITING);
|
|
|
|
//switch_mutex_unlock(tech_pvt->rtp_lock);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status channel_answer_channel(switch_core_session *session)
|
|
|
|
{
|
|
|
|
struct private_object *tech_pvt;
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
|
|
|
|
|
|
|
//}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-04-17 18:25:43 +00:00
|
|
|
|
|
|
|
static switch_status channel_receive_message(switch_core_session *session, switch_core_session_message *msg)
|
|
|
|
{
|
|
|
|
switch_channel *channel;
|
|
|
|
struct private_object *tech_pvt;
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
switch (msg->message_id) {
|
|
|
|
case SWITCH_MESSAGE_INDICATE_BRIDGE:
|
|
|
|
if (tech_pvt->rtp_session) {
|
|
|
|
switch_rtp_clear_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_USE_TIMER);
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "De-activate timed RTP!\n");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
|
|
|
|
if (tech_pvt->rtp_session) {
|
|
|
|
switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_USE_TIMER);
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Re-activate timed RTP!\n");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
static const switch_state_handler_table channel_event_handlers = {
|
|
|
|
/*.on_init */ channel_on_init,
|
|
|
|
/*.on_ring */ channel_on_ring,
|
|
|
|
/*.on_execute */ channel_on_execute,
|
|
|
|
/*.on_hangup */ channel_on_hangup,
|
|
|
|
/*.on_loopback */ channel_on_loopback,
|
|
|
|
/*.on_transmit */ channel_on_transmit
|
|
|
|
};
|
|
|
|
|
|
|
|
static const switch_io_routines channel_io_routines = {
|
|
|
|
/*.outgoing_channel */ channel_outgoing_channel,
|
|
|
|
/*.answer_channel */ channel_answer_channel,
|
|
|
|
/*.read_frame */ channel_read_frame,
|
|
|
|
/*.write_frame */ channel_write_frame,
|
|
|
|
/*.kill_channel */ channel_kill_channel,
|
|
|
|
/*.waitfor_read */ channel_waitfor_read,
|
|
|
|
/*.waitfor_write */ channel_waitfor_write,
|
2006-04-17 18:25:43 +00:00
|
|
|
/*.send_dtmf */ channel_send_dtmf,
|
|
|
|
/*.receive_message*/ channel_receive_message
|
2006-03-31 19:38:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const switch_endpoint_interface channel_endpoint_interface = {
|
|
|
|
/*.interface_name */ "dingaling",
|
|
|
|
/*.io_routines */ &channel_io_routines,
|
|
|
|
/*.event_handlers */ &channel_event_handlers,
|
|
|
|
/*.private */ NULL,
|
|
|
|
/*.next */ NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const switch_loadable_module_interface channel_module_interface = {
|
|
|
|
/*.module_name */ modname,
|
|
|
|
/*.endpoint_interface */ &channel_endpoint_interface,
|
|
|
|
/*.timer_interface */ NULL,
|
|
|
|
/*.dialplan_interface */ NULL,
|
|
|
|
/*.codec_interface */ NULL,
|
|
|
|
/*.application_interface */ NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
|
|
|
|
that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
|
|
|
|
*/
|
|
|
|
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile,
|
|
|
|
switch_core_session **new_session, switch_memory_pool *pool)
|
|
|
|
{
|
|
|
|
if ((*new_session = switch_core_session_request(&channel_endpoint_interface, pool)) != 0) {
|
|
|
|
struct private_object *tech_pvt;
|
|
|
|
switch_channel *channel;
|
|
|
|
switch_caller_profile *caller_profile = NULL;
|
|
|
|
struct mdl_profile *mdl_profile = NULL;
|
|
|
|
ldl_session_t *dlsession = NULL;
|
|
|
|
char *profile_name;
|
|
|
|
char *callto;
|
|
|
|
char idbuf[1024];
|
|
|
|
char *full_id;
|
|
|
|
char sess_id[11] = "";
|
|
|
|
char workspace[1024];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch_copy_string(workspace, outbound_profile->destination_number, sizeof(workspace));
|
|
|
|
profile_name = workspace;
|
|
|
|
if ((callto = strchr(profile_name, '/'))) {
|
|
|
|
*callto++ = '\0';
|
|
|
|
} else {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Invalid URL!\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(new_session);
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((mdl_profile = switch_core_hash_find(globals.profile_hash, profile_name))) {
|
|
|
|
if (!ldl_handle_ready(mdl_profile->handle)) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Doh! we are not logged in yet!\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(new_session);
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
}
|
|
|
|
if (!(full_id = ldl_handle_probe(mdl_profile->handle, callto, idbuf, sizeof(idbuf)))) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unknown Recipient!\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(new_session);
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
}
|
|
|
|
} else {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unknown Profile!\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(new_session);
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch_core_session_add_stream(*new_session, NULL);
|
2006-04-05 20:17:22 +00:00
|
|
|
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object))) != 0) {
|
2006-03-31 19:38:09 +00:00
|
|
|
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
|
|
|
channel = switch_core_session_get_channel(*new_session);
|
|
|
|
switch_core_session_set_private(*new_session, tech_pvt);
|
|
|
|
tech_pvt->session = *new_session;
|
|
|
|
tech_pvt->codec_index = -1;
|
2006-04-05 20:17:22 +00:00
|
|
|
tech_pvt->local_port = switch_rtp_request_port();
|
2006-03-31 19:38:09 +00:00
|
|
|
} else {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(new_session);
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (outbound_profile) {
|
|
|
|
char name[128];
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "DingaLing/%s-%04x", outbound_profile->destination_number, rand() & 0xffff);
|
|
|
|
switch_channel_set_name(channel, name);
|
|
|
|
|
|
|
|
caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
|
|
|
|
switch_channel_set_caller_profile(channel, caller_profile);
|
|
|
|
tech_pvt->caller_profile = caller_profile;
|
|
|
|
} else {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Doh! no caller profile\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(new_session);
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch_channel_set_flag(channel, CF_OUTBOUND);
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_OUTBOUND);
|
|
|
|
|
2006-04-04 16:07:40 +00:00
|
|
|
switch_stun_random_string(sess_id, 10, "0123456789");
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
ldl_session_create(&dlsession, mdl_profile->handle, sess_id, full_id, mdl_profile->login);
|
|
|
|
tech_pvt->profile = mdl_profile;
|
|
|
|
ldl_session_set_private(dlsession, *new_session);
|
|
|
|
tech_pvt->dlsession = dlsession;
|
|
|
|
get_codecs(tech_pvt);
|
2006-04-15 22:30:25 +00:00
|
|
|
//tech_pvt->desc_id = ldl_session_describe(dlsession, NULL, 0, LDL_DESCRIPTION_INITIATE);
|
2006-03-31 19:38:09 +00:00
|
|
|
negotiate_thread_launch(*new_session);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return SWITCH_STATUS_GENERR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "OH OH no pool\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return SWITCH_STATUS_TERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
load_config();
|
|
|
|
|
|
|
|
/* connect my internal structure to the blank pointer passed to me */
|
|
|
|
*interface = &channel_module_interface;
|
|
|
|
|
|
|
|
/* indicate that the module should continue to be loaded */
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ldl_status handle_loop(ldl_handle_t *handle)
|
|
|
|
{
|
|
|
|
if (!globals.running) {
|
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
return LDL_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
static void init_profile(struct mdl_profile *profile)
|
|
|
|
{
|
|
|
|
if (profile &&
|
|
|
|
profile->login &&
|
|
|
|
profile->password &&
|
|
|
|
profile->dialplan &&
|
|
|
|
profile->message &&
|
|
|
|
profile->ip &&
|
|
|
|
profile->name &&
|
|
|
|
profile->exten) {
|
|
|
|
ldl_handle_t *handle;
|
|
|
|
|
|
|
|
|
|
|
|
if (ldl_handle_init(&handle,
|
|
|
|
profile->login,
|
|
|
|
profile->password,
|
|
|
|
profile->message,
|
|
|
|
handle_loop,
|
|
|
|
handle_signalling,
|
|
|
|
handle_response,
|
|
|
|
profile) == LDL_STATUS_SUCCESS) {
|
|
|
|
profile->handle = handle;
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Started Thread for %s@%s\n", profile->login, profile->dialplan);
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_hash_insert(globals.profile_hash, profile->name, profile);
|
|
|
|
handle_thread_launch(handle);
|
|
|
|
}
|
|
|
|
} else {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Invalid Profile\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
|
|
|
|
{
|
|
|
|
if (globals.running) {
|
|
|
|
int x = 0;
|
|
|
|
globals.running = 0;
|
|
|
|
while (globals.handles > 0) {
|
|
|
|
switch_yield(100000);
|
|
|
|
x++;
|
|
|
|
if(x > 10) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ldl_global_destroy();
|
|
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static switch_status load_config(void)
|
|
|
|
{
|
|
|
|
switch_config cfg;
|
|
|
|
char *var, *val;
|
|
|
|
char *cf = "dingaling.conf";
|
|
|
|
struct mdl_profile *profile = NULL;
|
|
|
|
int lastcat = -1;
|
|
|
|
|
|
|
|
memset(&globals, 0, sizeof(globals));
|
|
|
|
globals.running = 1;
|
|
|
|
|
|
|
|
switch_core_hash_init(&globals.profile_hash, module_pool);
|
|
|
|
if (!switch_config_open_file(&cfg, cf)) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "open of %s failed\n", cf);
|
2006-03-31 19:38:09 +00:00
|
|
|
return SWITCH_STATUS_TERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (switch_config_next_pair(&cfg, &var, &val)) {
|
|
|
|
if (!strcasecmp(cfg.category, "settings")) {
|
|
|
|
if (!strcmp(var, "debug")) {
|
|
|
|
globals.debug = atoi(val);
|
|
|
|
} else if (!strcmp(var, "codec_prefs")) {
|
|
|
|
set_global_codec_string(val);
|
|
|
|
globals.codec_order_last =
|
|
|
|
switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS);
|
|
|
|
} else if (!strcmp(var, "codec_rates")) {
|
|
|
|
set_global_codec_rates_string(val);
|
|
|
|
globals.codec_rates_last =
|
|
|
|
switch_separate_string(globals.codec_rates_string, ',', globals.codec_rates, SWITCH_MAX_CODECS);
|
|
|
|
}
|
|
|
|
} else if (!strcasecmp(cfg.category, "interface")) {
|
|
|
|
if (!globals.init) {
|
|
|
|
ldl_global_init(globals.debug);
|
2006-04-14 02:05:29 +00:00
|
|
|
ldl_global_set_logger(dl_logger);
|
2006-03-31 19:38:09 +00:00
|
|
|
globals.init = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.catno != lastcat) {
|
|
|
|
if (profile) {
|
|
|
|
init_profile(profile);
|
|
|
|
profile = NULL;
|
|
|
|
}
|
|
|
|
lastcat = cfg.catno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!profile) {
|
|
|
|
profile = switch_core_alloc(module_pool, sizeof(*profile));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(var, "login")) {
|
|
|
|
profile->login = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "password")) {
|
|
|
|
profile->password = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "dialplan")) {
|
|
|
|
profile->dialplan = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "name")) {
|
|
|
|
profile->name = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "message")) {
|
|
|
|
profile->message = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "ip")) {
|
|
|
|
profile->ip = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "extip")) {
|
|
|
|
profile->extip = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "lanaddr")) {
|
|
|
|
profile->lanaddr = switch_core_strdup(module_pool, val);
|
|
|
|
} else if (!strcmp(var, "exten")) {
|
|
|
|
profile->exten = switch_core_strdup(module_pool, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (profile) {
|
|
|
|
init_profile(profile);
|
|
|
|
profile = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!globals.dialplan) {
|
|
|
|
set_global_dialplan("default");
|
|
|
|
}
|
|
|
|
|
|
|
|
switch_config_close_file(&cfg);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsession, ldl_signal_t signal, char *msg)
|
|
|
|
{
|
|
|
|
struct mdl_profile *profile = NULL;
|
|
|
|
switch_core_session *session = NULL;
|
|
|
|
switch_channel *channel = NULL;
|
|
|
|
struct private_object *tech_pvt = NULL;
|
|
|
|
char *advip;
|
|
|
|
|
|
|
|
assert(dlsession != NULL);
|
|
|
|
assert(handle != NULL);
|
|
|
|
|
|
|
|
if (!(profile = ldl_handle_get_private(handle))) {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR NO PROFILE!\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((session = ldl_session_get_private(dlsession))) {
|
|
|
|
tech_pvt = switch_core_session_get_private(session);
|
|
|
|
assert(tech_pvt != NULL);
|
|
|
|
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
assert(channel != NULL);
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "using Existing session for %s\n", ldl_session_get_id(dlsession));
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
if (switch_channel_get_state(channel) >= CS_HANGUP) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Call %s is already over\n", switch_channel_get_name(channel));
|
2006-03-31 19:38:09 +00:00
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (signal != LDL_SIGNAL_INITIATE) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session is already dead\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
if ((session = switch_core_session_request(&channel_endpoint_interface, NULL)) != 0) {
|
|
|
|
switch_core_session_add_stream(session, NULL);
|
|
|
|
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(session, sizeof(struct private_object))) != 0) {
|
|
|
|
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
switch_core_session_set_private(session, tech_pvt);
|
|
|
|
tech_pvt->session = session;
|
|
|
|
tech_pvt->codec_index = -1;
|
|
|
|
tech_pvt->profile = profile;
|
2006-04-05 20:17:22 +00:00
|
|
|
tech_pvt->local_port = switch_rtp_request_port();
|
2006-04-15 22:30:25 +00:00
|
|
|
switch_set_flag(tech_pvt, TFLAG_ANSWER);
|
2006-03-31 19:38:09 +00:00
|
|
|
} else {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Hey where is my memory pool?\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(&session);
|
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Creating a session for %s\n", ldl_session_get_id(dlsession));
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
if ((tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
|
|
|
|
profile->dialplan,
|
|
|
|
ldl_session_get_caller(dlsession),
|
|
|
|
ldl_session_get_caller(dlsession),
|
|
|
|
ldl_session_get_ip(dlsession),
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
profile->exten)) != 0) {
|
|
|
|
char name[128];
|
|
|
|
snprintf(name, sizeof(name), "DingaLing/%s-%04x", tech_pvt->caller_profile->destination_number,
|
|
|
|
rand() & 0xffff);
|
|
|
|
switch_channel_set_name(channel, name);
|
|
|
|
switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ldl_session_set_private(dlsession, session);
|
|
|
|
tech_pvt->dlsession = dlsession;
|
|
|
|
negotiate_thread_launch(session);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
advip = profile->extip ? profile->extip : profile->ip;
|
|
|
|
|
|
|
|
switch(signal) {
|
|
|
|
case LDL_SIGNAL_NONE:
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
break;
|
|
|
|
case LDL_SIGNAL_MSG:
|
|
|
|
if (msg) {
|
2006-04-16 06:05:53 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MSG [%s]\n", msg);
|
2006-03-31 19:38:09 +00:00
|
|
|
if (*msg == '+') {
|
|
|
|
switch_channel_queue_dtmf(channel, msg + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LDL_SIGNAL_INITIATE:
|
|
|
|
if (signal) {
|
|
|
|
ldl_payload_t *payloads;
|
|
|
|
unsigned int len = 0;
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
|
|
|
if (!strcasecmp(msg, "accept")) {
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_ANSWER);
|
|
|
|
do_candidates(tech_pvt, 0);
|
|
|
|
}
|
|
|
|
}
|
2006-04-15 05:16:24 +00:00
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
if (tech_pvt->codec_index > -1) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Already decided on a codec\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tech_pvt->num_codecs) {
|
|
|
|
get_codecs(tech_pvt);
|
|
|
|
if (!tech_pvt->num_codecs) {
|
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-15 22:30:25 +00:00
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
if (ldl_session_get_payloads(dlsession, &payloads, &len) == LDL_STATUS_SUCCESS) {
|
|
|
|
unsigned int x, y;
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%u payloads\n", len);
|
2006-03-31 19:38:09 +00:00
|
|
|
for(x = 0; x < len; x++) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Available Payload %s %u\n", payloads[x].name, payloads[x].id);
|
2006-03-31 19:38:09 +00:00
|
|
|
for(y = 0; y < tech_pvt->num_codecs; y++) {
|
|
|
|
if (payloads[x].id == tech_pvt->codecs[y]->ianacode) {
|
|
|
|
tech_pvt->codec_index = y;
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Choosing Payload index %u %s %u\n", y, payloads[x].name, payloads[x].id);
|
2006-03-31 19:38:09 +00:00
|
|
|
tech_pvt->codec_name = tech_pvt->codecs[y]->iananame;
|
|
|
|
tech_pvt->codec_num = tech_pvt->codecs[y]->ianacode;
|
2006-04-15 22:30:25 +00:00
|
|
|
if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
|
|
|
do_describe(tech_pvt, 0);
|
|
|
|
}
|
2006-03-31 19:38:09 +00:00
|
|
|
return LDL_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-04-15 22:30:25 +00:00
|
|
|
|
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
break;
|
|
|
|
case LDL_SIGNAL_CANDIDATES:
|
|
|
|
if (signal) {
|
|
|
|
ldl_candidate_t *candidates;
|
|
|
|
unsigned int len = 0;
|
|
|
|
|
|
|
|
if (ldl_session_get_candidates(dlsession, &candidates, &len) == LDL_STATUS_SUCCESS) {
|
|
|
|
unsigned int x;
|
|
|
|
|
|
|
|
|
|
|
|
if (tech_pvt->remote_ip) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Already picked an IP [%s]\n", tech_pvt->remote_ip);
|
2006-03-31 19:38:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%u candidates\n", len);
|
2006-03-31 19:38:09 +00:00
|
|
|
for(x = 0; x < len; x++) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "candidates %s:%d\n", candidates[x].address, candidates[x].port);
|
2006-04-15 05:00:51 +00:00
|
|
|
if (!strcasecmp(candidates[x].protocol, "udp") && (!strcasecmp(candidates[x].type, "local") || !strcasecmp(candidates[x].type, "stun")) &&
|
2006-03-31 19:38:09 +00:00
|
|
|
((profile->lanaddr && !strncasecmp(candidates[x].address, profile->lanaddr, strlen(profile->lanaddr))) ||
|
2006-04-13 23:19:33 +00:00
|
|
|
(strncasecmp(candidates[x].address, "10.", 3) &&
|
|
|
|
strncasecmp(candidates[x].address, "192.168.", 8) &&
|
|
|
|
strncasecmp(candidates[x].address, "127.", 4) &&
|
|
|
|
strncasecmp(candidates[x].address, "1.", 2) &&
|
|
|
|
strncasecmp(candidates[x].address, "2.", 2) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.16.", 7) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.17.", 7) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.18.", 7) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.19.", 7) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.2", 5) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.30.", 7) &&
|
|
|
|
strncasecmp(candidates[x].address, "172.31.", 7)
|
|
|
|
))) {
|
2006-03-31 19:38:09 +00:00
|
|
|
ldl_payload_t payloads[5];
|
|
|
|
|
|
|
|
memset(payloads, 0, sizeof(payloads));
|
|
|
|
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Acceptable Candidate %s:%d\n", candidates[x].address, candidates[x].port);
|
2006-03-31 19:38:09 +00:00
|
|
|
|
|
|
|
if (!tech_pvt->num_codecs) {
|
|
|
|
get_codecs(tech_pvt);
|
|
|
|
if (!tech_pvt->num_codecs) {
|
|
|
|
return LDL_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tech_pvt->remote_ip = switch_core_session_strdup(session, candidates[x].address);
|
2006-03-31 21:47:44 +00:00
|
|
|
ldl_session_set_ip(dlsession, tech_pvt->remote_ip);
|
2006-03-31 19:38:09 +00:00
|
|
|
tech_pvt->remote_port = candidates[x].port;
|
|
|
|
tech_pvt->remote_user = switch_core_session_strdup(session, candidates[x].username);
|
2006-04-15 22:30:25 +00:00
|
|
|
if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
|
|
|
do_candidates(tech_pvt, 0);
|
|
|
|
}
|
|
|
|
switch_set_flag(tech_pvt, TFLAG_TRANSPORT);
|
|
|
|
|
2006-03-31 19:38:09 +00:00
|
|
|
return LDL_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LDL_SIGNAL_ERROR:
|
|
|
|
case LDL_SIGNAL_TERMINATE:
|
|
|
|
if (channel) {
|
|
|
|
switch_channel_state state = switch_channel_get_state(channel);
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "hungup %s %u %d\n", switch_channel_get_name(channel), state, CS_INIT);
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_set_flag(tech_pvt, TFLAG_BYE);
|
|
|
|
switch_clear_flag(tech_pvt, TFLAG_IO);
|
|
|
|
switch_channel_hangup(channel);
|
|
|
|
|
|
|
|
if (state <= CS_INIT && !switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroy unused Session\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
switch_core_session_destroy(&session);
|
|
|
|
} else {
|
2006-04-11 21:13:44 +00:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "End Call\n");
|
2006-03-31 19:38:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LDL_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ldl_status handle_response(ldl_handle_t *handle, char *id)
|
|
|
|
{
|
|
|
|
return LDL_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|