/* * Asterisk -- A telephony toolkit for Linux. * * Direct rtp connection channel * * 2008-2009, Ivan Volnikov * * This program is free software, distributed under the terms of * the GNU General Public License * */ /*! \file * * \author Ivan Volnikov * * \brief RTP Channel Driver * * \ingroup channel_drivers */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 136888 $") #include #include #include #include #include #include #include #include "asterisk/lock.h" #include "asterisk/channel.h" #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" #include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" #include "asterisk/cli.h" #include "asterisk/app.h" #include "asterisk/manager.h" #include "asterisk/stringfields.h" #include "asterisk/utils.h" #include "asterisk/causes.h" #include "asterisk/astobj.h" #include "asterisk/abstract_jb.h" #define DIR_RTP_CONFIG "chan_rtp.conf" /*! Global jitterbuffer configuration - by default, jb is disabled */ static struct ast_jb_conf default_jbconf = { .flags = 0, .max_size = -1, .resync_threshold = -1, .impl = "" }; static struct ast_jb_conf global_jbconf; struct dir_rtp_pvt { ast_mutex_t lock; /*!< Channel private lock */ struct dir_rtp *parent; /*!< Parent client */ struct sockaddr_in us; struct sockaddr_in them; format_t capability; char ring[10]; /*!< Message ID of ring */ int initiator; /*!< If we're the initiator */ int isruntime; struct ast_codec_pref prefs; char cid_num[80]; /*!< Caller ID num */ char cid_name[80]; /*!< Caller ID name */ char exten[AST_MAX_EXTENSION]; /*!< Called extension */ struct ast_channel *owner; /*!< Master Channel */ char audio_content_name[100]; /*!< name attribute of content tag */ struct ast_rtp *rtp; /*!< RTP audio session */ char video_content_name[100]; /*!< name attribute of content tag */ struct ast_rtp *vrtp; /*!< RTP video session */ int jointcapability; /*!< Supported capability at both ends (codecs ) */ int peercapability; struct dir_rtp_pvt *next; /* Next entity */ }; struct dir_rtp { ASTOBJ_COMPONENTS(struct dir_rtp); struct dir_rtp_pvt *p; struct ast_codec_pref prefs; int amaflags; /*!< AMA Flags */ char user[100]; struct sockaddr_in us; struct sockaddr_in them; char context[AST_MAX_CONTEXT]; char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */ format_t capability; ast_group_t callgroup; /*!< Call group */ ast_group_t pickupgroup; /*!< Pickup group */ int callingpres; /*!< Calling presentation */ int allowguest; int isruntime; char language[MAX_LANGUAGE]; /*!< Default language for prompts */ char musicclass[MAX_MUSICCLASS]; /*!< Music on Hold class */ char parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */ }; struct dir_rtp_container { ASTOBJ_CONTAINER_COMPONENTS(struct dir_rtp); }; static const char desc[] = "Direct RTP Channel"; static const char channel_type[] = "RTP"; static format_t default_codec = AST_FORMAT_ALAW; static char default_context[AST_MAX_CONTEXT] = "default"; static struct ast_codec_pref default_prefs; AST_MUTEX_DEFINE_STATIC(dir_rtplock); /*!< Protect the interface list (of dir_rtp_pvt's) */ /* Forward declarations */ /*channel interface*/ static struct ast_channel *dir_rtp_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause); static int dir_rtp_call(struct ast_channel *ast, char *dest, int timeout); static int dir_rtp_hangup(struct ast_channel *ast); static int dir_rtp_answer(struct ast_channel *ast); static struct ast_frame *dir_rtp_read(struct ast_channel *ast); static int dir_rtp_write(struct ast_channel *ast, struct ast_frame *f); static int dir_rtp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static char *dir_rtp_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *dir_rtp_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static struct dir_rtp_pvt *dir_rtp_alloc(struct dir_rtp *client); static struct dir_rtp *dir_rtp_create_runtime_channel(char *name, char *local_ip, int local_port, char *remote_ip, int remote_port, int capability); /* TMP */ static int dir_rtp_load_config(void); /*----- RTP interface functions */ static int dir_rtp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active); static enum ast_rtp_get_result dir_rtp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); static int dir_rtp_get_codec(struct ast_channel *chan); /*! \brief PBX interface structure for channel registration */ static const struct ast_channel_tech dir_rtp_tech = { .type = "RTP", .description = "Direct RTP Channel Driver", .capabilities = AST_FORMAT_AUDIO_MASK, .requester = dir_rtp_request, .bridge = ast_rtp_bridge, .call = dir_rtp_call, .hangup = dir_rtp_hangup, .answer = dir_rtp_answer, .read = dir_rtp_read, .write = dir_rtp_write, .exception = dir_rtp_read, .fixup = dir_rtp_fixup, .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER }; struct ast_sockaddr bindaddr; /*!< The address we bind to */ static struct sched_context *sched; /*!< The scheduling context */ static struct io_context *io; /*!< The IO context */ static struct ast_sockaddr internip; /*! \brief RTP driver interface */ static struct ast_rtp_protocol dir_rtp_rtp = { type: "RTP", get_rtp_info: dir_rtp_get_rtp_peer, set_rtp_peer: dir_rtp_set_rtp_peer, get_codec: dir_rtp_get_codec, }; static struct ast_cli_entry dir_rtp_cli[] = { AST_CLI_DEFINE(dir_rtp_do_reload, "Reload Direct RTP configuration"), AST_CLI_DEFINE(dir_rtp_show_channels, "Show Direct RTP channels"), }; static struct dir_rtp_container dir_rtp_list; static void dir_rtp_rtppeer_destroy(struct dir_rtp *obj) { ast_free(obj); } static struct dir_rtp *find_dir_rtp(char *name) { struct dir_rtp *dir_rtp = NULL; dir_rtp = ASTOBJ_CONTAINER_FIND(&dir_rtp_list, name); if (!dir_rtp && strchr(name, '@')) dir_rtp = ASTOBJ_CONTAINER_FIND_FULL(&dir_rtp_list, name, user, , , strcasecmp); return dir_rtp; } static int dir_rtp_answer(struct ast_channel *chan) { struct dir_rtp_pvt *p = chan->tech_pvt; int res = 0; ast_mutex_lock(&p->lock); //ToDo ast_log(LOG_NOTICE, "Answer! chan=%s\n", chan->name); ast_mutex_unlock(&p->lock); return res; } static enum ast_rtp_get_result dir_rtp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) { struct dir_rtp_pvt *p = chan->tech_pvt; enum ast_rtp_get_result res = AST_RTP_GET_FAILED; if (!p) return res; ast_mutex_lock(&p->lock); if (p->rtp) { *rtp = p->rtp; res = AST_RTP_TRY_PARTIAL; } ast_mutex_unlock(&p->lock); return res; } static int dir_rtp_get_codec(struct ast_channel *chan) { struct dir_rtp_pvt *p = chan->tech_pvt; return p->peercapability; } static int dir_rtp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active) { struct dir_rtp_pvt *p; p = chan->tech_pvt; if (!p) return -1; ast_mutex_lock(&p->lock); //ToDo ast_log(LOG_NOTICE, "set_rtp_peer: chan=%s\n", chan->name); //ast_rtp_get_peer(rtp, &p->them); //ast_log(LOG_NOTICE, "Peer = %s:%d\n", ast_inet_ntoa(&p->them.sin_addr), ntohs(&p->them.sin_port)); //ast_rtp_get_us(rtp, &p->us); ast_mutex_unlock(&p->lock); return 0; } static struct dir_rtp_pvt *dir_rtp_alloc(struct dir_rtp *client) { struct dir_rtp_pvt *tmp = NULL; ast_debug(1, "The client is %s for alloc\n", client->name); if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { return NULL; } memcpy(&tmp->prefs, &client->prefs, sizeof(tmp->prefs)); tmp->capability = client->capability; tmp->isruntime = client->isruntime; memcpy(&tmp->us, &client->us, sizeof(client->us)); memcpy(&tmp->them, &client->them, sizeof(client->them)); //snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random()); //tmp->initiator = 1; if (tmp->us.sin_port) tmp->rtp = ast_rtp_new_with_bindaddr_port(sched, io, 1, 0, tmp->us.sin_addr, ntohs(tmp->us.sin_port)); //tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, tmp->us.sin_addr); else tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, tmp->us.sin_addr); tmp->parent = client; if (!tmp->rtp) { ast_log(LOG_WARNING, "Out of RTP sessions?\n"); ast_free(tmp); return NULL; } if (tmp->rtp && tmp->them.sin_port) { ast_rtp_set_peer(tmp->rtp, &tmp->them); ast_log(LOG_NOTICE , "Peer audio RTP is %s:%d\n", ast_inet_ntoa(tmp->them.sin_addr), ntohs(tmp->them.sin_port)); } ast_copy_string(tmp->exten, "s", sizeof(tmp->exten)); ast_mutex_init(&tmp->lock); ast_mutex_lock(&dir_rtplock); tmp->next = client->p; client->p = tmp; ast_mutex_unlock(&dir_rtplock); return tmp; } /*! \brief Start new Direct RTP channel */ static struct ast_channel *dir_rtp_new(struct dir_rtp *client, struct dir_rtp_pvt *i, int state) { struct ast_channel *tmp; int fmt; int what; tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->exten, client->context, 0, client->amaflags, "RTP/%s-%04lx", client->name, ast_random() & 0xffff); if (!tmp) { ast_log(LOG_WARNING, "Unable to allocate Direct RTP channel structure!\n"); return NULL; } else ast_log(LOG_NOTICE, "Allocated RTP channel structure\n"); tmp->tech = &dir_rtp_tech; /* Select our native format based on codec preference until we receive something from another device to the contrary. */ if (i->jointcapability) what = i->jointcapability; else if (i->capability) what = i->capability; else what = default_codec; ast_log(LOG_NOTICE, "wtf?=%s\n", ast_getformatname(what)); /* Set Frame packetization */ if (i->rtp) ast_rtp_codec_setpref(i->rtp, &i->prefs); tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) /*| (i->jointcapability & AST_FORMAT_VIDEO_MASK)*/; fmt = tmp->nativeformats;//ast_best_codec(tmp->nativeformats); if (i->rtp) { ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); } if (i->vrtp) { ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); } if (state == AST_STATE_RING) tmp->rings = 1; tmp->adsicpe = AST_ADSI_UNAVAILABLE; tmp->writeformat = fmt; tmp->rawwriteformat = fmt; tmp->readformat = fmt; tmp->rawreadformat = fmt; tmp->tech_pvt = i; tmp->callgroup = client->callgroup; tmp->pickupgroup = client->pickupgroup; //tmp->cid.cid_pres = client->callingpres; tmp->caller.id.name.presentation = client->callingpres; tmp->caller.id.number.presentation = client->callingpres; if (!ast_strlen_zero(client->accountcode)) ast_string_field_set(tmp, accountcode, client->accountcode); if (client->amaflags) tmp->amaflags = client->amaflags; if (!ast_strlen_zero(client->language)) ast_string_field_set(tmp, language, client->language); if (!ast_strlen_zero(client->musicclass)) ast_string_field_set(tmp, musicclass, client->musicclass); i->owner = tmp; ast_copy_string(tmp->context, client->context, sizeof(tmp->context)); ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten)); /* Don't use ast_set_callerid() here because it will * generate an unnecessary NewCallerID event */ if (!ast_strlen_zero(i->cid_num)) { tmp->caller.ani.number.valid = 1; tmp->caller.ani.number.str = ast_strdup(i->cid_num); } if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) { tmp->dialed.number.str = ast_strdup(i->exten); } tmp->priority = 1; if (i->rtp) ast_jb_configure(tmp, &global_jbconf); if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) { ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION; ast_hangup(tmp); tmp = NULL; } else ast_log(LOG_NOTICE, "Started PBX on %s\n", tmp->name); return tmp; } static void dir_rtp_free_pvt(struct dir_rtp *client, struct dir_rtp_pvt *p) { struct dir_rtp_pvt *cur, *prev = NULL; cur = client->p; while (cur) { if (cur == p) { if (prev) prev->next = p->next; else client->p = p->next; break; } prev = cur; cur = cur->next; } if (p->owner) ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); if (p->rtp) ast_rtp_destroy(p->rtp); if (p->vrtp) ast_rtp_destroy(p->vrtp); ast_free(p); } static struct ast_frame *dir_rtp_rtp_read(struct ast_channel *ast, struct dir_rtp_pvt *p) { struct ast_frame *f; if (!p->rtp) return &ast_null_frame; f = ast_rtp_read(p->rtp); if (p->owner) { /* We already hold the channel lock */ if (f->frametype == AST_FRAME_VOICE) { if (f->subclass.codec != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) { ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec)); p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK) | f->subclass.codec; ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } } } return f; } static struct ast_frame *dir_rtp_read(struct ast_channel *ast) { struct ast_frame *fr; struct dir_rtp_pvt *p = ast->tech_pvt; ast_mutex_lock(&p->lock); fr = dir_rtp_rtp_read(ast, p); ast_mutex_unlock(&p->lock); return fr; } /*! \brief Send frame to media channel (rtp) */ static int dir_rtp_write(struct ast_channel *ast, struct ast_frame *frame) { struct dir_rtp_pvt *p = ast->tech_pvt; int res = 0; switch (frame->frametype) { case AST_FRAME_VOICE: if (!(frame->subclass.codec & ast->nativeformats)) { char s1[512], s2[512], s3[512]; ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s read/write = %s/%s\n", ast_getformatname(frame->subclass.codec), ast_getformatname_multiple(s1, sizeof(s1), ast->nativeformats & AST_FORMAT_AUDIO_MASK), ast_getformatname_multiple(s2, sizeof(s2), ast->readformat), ast_getformatname_multiple(s3, sizeof(s3), ast->writeformat)); return 0; } if (p) { ast_mutex_lock(&p->lock); if (p->rtp) { res = ast_rtp_write(p->rtp, frame); } ast_mutex_unlock(&p->lock); } break; case AST_FRAME_VIDEO: if (p) { ast_mutex_lock(&p->lock); if (p->vrtp) { res = ast_rtp_write(p->vrtp, frame); } ast_mutex_unlock(&p->lock); } break; case AST_FRAME_IMAGE: return 0; break; default: ast_log(LOG_WARNING, "Can't send %d type frames with Direct RTP write\n", frame->frametype); return 0; } return res; } static int dir_rtp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct dir_rtp_pvt *p = newchan->tech_pvt; ast_mutex_lock(&p->lock); if ((p->owner != oldchan)) { ast_mutex_unlock(&p->lock); return -1; } if (p->owner == oldchan) p->owner = newchan; ast_mutex_unlock(&p->lock); return 0; } /*! \brief Initiate new call, part of PBX interface * dest is the dial string */ static int dir_rtp_call(struct ast_channel *ast, char *dest, int timeout) { //struct dir_rtp_pvt *p = ast->tech_pvt; ast_setstate(ast, AST_STATE_UP); //ToDo return 0; } /*! \brief Hangup a call through the RTP channel */ static int dir_rtp_hangup(struct ast_channel *ast) { struct dir_rtp_pvt *p = ast->tech_pvt; struct dir_rtp *client; ast_mutex_lock(&p->lock); client = p->parent; p->owner = NULL; ast->tech_pvt = NULL; ast_mutex_unlock(&p->lock); dir_rtp_free_pvt(client, p); return 0; } /*! \brief Part of PBX interface */ static struct ast_channel *dir_rtp_request(const char *request_type, format_t format, const struct ast_channel *requestor, void *data, int *cause) { struct dir_rtp_pvt *p = NULL; struct dir_rtp *client = NULL; char *rtplocal = NULL, *rtpremote = NULL, *rtpcodec = NULL, *s = NULL; char *rtplocal_ip = NULL, *rtpremote_ip = NULL; int rtplocal_port = 0, rtpremote_port = 0; struct ast_channel *chan = NULL; int newcapability = 0; //int trimlastchar = 0; /* * Possible dialstrings: * RTP/// * RTP/// * If no codec specified, config default is used */ if (data) { s = ast_strdupa(data); if (s) { rtplocal = strsep(&s, "/"); if (rtplocal && (rtplocal[0] != '\0')) rtpremote = strsep(&s, "/"); if (rtpremote && (rtpremote[0] != '\0')) rtpcodec = strsep(&s, "/"); //trimlastchar = (strlen(s) == 1 && s[0] == '/') ? 1 : 0; ast_log(LOG_NOTICE, "RTP dialstring: local=%s, remote=%s, codec=%s\n", rtplocal, rtpremote, rtpcodec); /* parse rtp params */ if (!rtplocal || !strchr(rtpremote, ':')) { ast_log(LOG_WARNING, "Bad arguments in RTP dialstring: %s\n", (char*) data); return NULL; } // rtplocal client = find_dir_rtp(rtplocal); if (!client) { s = ast_strdupa(rtplocal); rtplocal_ip = strsep(&s, ":"); if (rtplocal_ip && (rtplocal_ip[0] != '\0')) rtplocal_port = atoi(strsep(&s, ":")); } // rtpremote s = ast_strdupa(rtpremote); rtpremote_ip = strsep(&s, ":"); if (rtpremote_ip && (rtpremote_ip[0] != '\0')) rtpremote_port = atoi(strsep(&s, ":")); // rtpcodec if (!ast_strlen_zero(rtpcodec)) { if (!(newcapability = ast_getformatbyname(rtpcodec))) newcapability = atoi(rtpcodec); } } } //client = find_dir_rtp(rtplocal); if (!client) { ast_log(LOG_WARNING, "Could not find local named RTP channel. New channel will be created.\n"); if (!(client = dir_rtp_create_runtime_channel( (char*) data, rtplocal_ip, rtplocal_port, rtpremote_ip, rtpremote_port, newcapability))) { ast_log(LOG_ERROR, "Could not create new channel.\n"); return NULL; } } ASTOBJ_WRLOCK(client); p = dir_rtp_alloc(client); if (p) { if (newcapability) { ast_codec_pref_prepend(&p->prefs, newcapability, 0); p->capability = newcapability; } chan = dir_rtp_new(client, p, AST_STATE_DOWN); } ASTOBJ_UNLOCK(client); if (chan) ast_log(LOG_NOTICE, "rtpchan=%s\n", chan->name); return chan; } /*! \brief CLI command "dir_rtp show channels" */ static char *dir_rtp_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { #define FORMAT "%-60.60s %-8.8s %-16.16s %-8.8s %-8.8s \n" struct dir_rtp_pvt *p; struct ast_channel *chan; int numchans = 0; switch (cmd) { case CLI_INIT: e->command = "dir_rtp show channels"; e->usage = "Usage: dir_rtp show channels\n" " Shows current state of the Direct RTP channels.\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) return CLI_SHOWUSAGE; //ToDo ast_mutex_lock(&dir_rtplock); ast_cli(a->fd, FORMAT, "Channel", "Codec", "Context", "RdFmt", "WrFmt"); ASTOBJ_CONTAINER_TRAVERSE(&dir_rtp_list, 1, { ASTOBJ_WRLOCK(iterator); p = iterator->p; while(p) { //ast_log(LOG_NOTICE, "p->capability=%d\n", p->capability); chan = p->owner; /*ast_copy_string(them, p->them, sizeof(them));*/ if (chan) ast_cli(a->fd, FORMAT, chan->name, ast_getformatname(p->capability), chan->context, ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat) ); else ast_log(LOG_WARNING, "Not available channel\n"); numchans ++; p = p->next; } ASTOBJ_UNLOCK(iterator); }); ast_mutex_unlock(&dir_rtplock); ast_cli(a->fd, "%d active direct RTP channel%s\n", numchans, (numchans != 1) ? "s" : ""); return CLI_SUCCESS; #undef FORMAT } /*! \brief CLI command "dir_rtp reload" */ static char *dir_rtp_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { switch (cmd) { case CLI_INIT: e->command = "dir_rtp reload"; e->usage = "Usage: dir_rtp reload\n" " Reload Direct RTP channel driver.\n"; return NULL; case CLI_GENERATE: return NULL; } dir_rtp_load_config(); ast_log(LOG_NOTICE, "ToDo\n"); return CLI_SUCCESS; } static struct dir_rtp *dir_rtp_create_runtime_channel(char *name, char *local_ip, int local_port, char *remote_ip, int remote_port, int capability) { struct dir_rtp *rtppeer = NULL; struct hostent *hp; struct ast_hostent ahp; ast_log(LOG_NOTICE, "Creating runtime Direct RTP channel\n"); rtppeer = ast_calloc(1, sizeof(*rtppeer)); if (!rtppeer) { ast_log(LOG_ERROR, "Out of memory.\n"); return NULL; } ASTOBJ_INIT(rtppeer); ASTOBJ_WRLOCK(rtppeer); rtppeer->isruntime = 1; ast_copy_string(rtppeer->name, name, sizeof(rtppeer->name)); ast_copy_string(rtppeer->context, default_context, sizeof(rtppeer->context)); if (capability) { ast_parse_allow_disallow(&rtppeer->prefs, &rtppeer->capability, ast_getformatname(capability), 1); //rtppeer->capability = capability; } else rtppeer->prefs = default_prefs; //memcpy(&rtppeer->us, &us, sizeof(rtppeer->us)); if (!(hp = ast_gethostbyname(local_ip, &ahp))) ast_log(LOG_WARNING, "Invalid address: %s\n", local_ip); else memcpy(&rtppeer->us.sin_addr, hp->h_addr, sizeof(rtppeer->us.sin_addr)); rtppeer->us.sin_port = htons(local_port); //memcpy(&rtppeer->them, &them, sizeof(rtppeer->them)); if (!(hp = ast_gethostbyname(remote_ip, &ahp))) ast_log(LOG_WARNING, "Invalid address: %s\n", remote_ip); else memcpy(&rtppeer->them.sin_addr, hp->h_addr, sizeof(rtppeer->them.sin_addr)); rtppeer->them.sin_port = htons(remote_port); ast_log(LOG_NOTICE, "Creating runtime Direct RTP channel 1 ->%s\n", rtppeer->name); ASTOBJ_UNLOCK(rtppeer); ASTOBJ_CONTAINER_LINK(&dir_rtp_list, rtppeer); //ASTOBJ_UNREF(rtppeer, dir_rtp_rtppeer_destroy); ast_log(LOG_NOTICE, "Creating runtime Direct RTP channel 2 ->%s\n", rtppeer->name); return rtppeer; } static int dir_rtp_load_config(void) { char *cat = NULL; struct ast_config *cfg = NULL; struct ast_variable *var; struct dir_rtp *rtppeer; struct ast_flags config_flags = { 0 }; struct hostent *hp; struct ast_hostent ahp; ast_log(LOG_NOTICE, "Loading config %s...\n", DIR_RTP_CONFIG); cfg = ast_config_load(DIR_RTP_CONFIG, config_flags); if (!cfg) return 0; /* Copy the default jb config over global_jbconf */ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); cat = ast_category_browse(cfg, NULL); ast_log(LOG_NOTICE, "%s\n", cat); for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { /* handle jb conf */ if (!ast_jb_read_conf(&global_jbconf, var->name, var->value)) continue; if (!strcasecmp(var->name, "codec")) ast_parse_allow_disallow(&default_prefs, &default_codec, var->value, 1); else if (!strcasecmp(var->name, "context")) ast_copy_string(default_context, var->value, sizeof(default_context)); else if (!strcasecmp(var->name, "bindaddr")) { if (!(hp = ast_gethostbyname(var->value, &ahp))) ast_log(LOG_WARNING, "Invalid address: %s\n", var->value); else memcpy(&bindaddr, hp->h_addr, sizeof(bindaddr)); } } while (cat) { if (strcasecmp(cat, "general")) { ast_log(LOG_NOTICE, "%s\n", cat); var = ast_variable_browse(cfg, cat); rtppeer = ast_calloc(1, sizeof(*rtppeer)); if (!rtppeer) ast_log(LOG_WARNING, "Out of memory.\n"); ASTOBJ_INIT(rtppeer); ASTOBJ_WRLOCK(rtppeer); ast_copy_string(rtppeer->name, cat, sizeof(rtppeer->name)); ast_copy_string(rtppeer->context, default_context, sizeof(rtppeer->context)); rtppeer->prefs = default_prefs; memcpy(&rtppeer->us, &bindaddr, sizeof(rtppeer->us)); while (var) { ast_log(LOG_NOTICE, "\t%s = %s\n", var->name, var->value); if (!strcasecmp(var->name, "codec")) ast_parse_allow_disallow(&rtppeer->prefs, &rtppeer->capability, var->value, 1); else if (!strcasecmp(var->name, "context")) ast_copy_string(rtppeer->context, var->value, sizeof(rtppeer->context)); else if (!strcasecmp(var->name, "port")) rtppeer->them.sin_port = htons(atoi(var->value)); else if (!strcasecmp(var->name, "bindport")) rtppeer->us.sin_port = htons(atoi(var->value)); else if (!strcasecmp(var->name, "host")) { if (!(hp = ast_gethostbyname(var->value, &ahp))) ast_log(LOG_WARNING, "Invalid address: %s\n", var->value); else memcpy(&rtppeer->them.sin_addr, hp->h_addr, sizeof(rtppeer->them.sin_addr)); } else if (!strcasecmp(var->name, "bindaddr")) { if (!(hp = ast_gethostbyname(var->value, &ahp))) ast_log(LOG_WARNING, "Invalid address: %s\n", var->value); else memcpy(&rtppeer->us.sin_addr, hp->h_addr, sizeof(rtppeer->us.sin_addr)); } var = var->next; } ASTOBJ_UNLOCK(rtppeer); ASTOBJ_CONTAINER_LINK(&dir_rtp_list, rtppeer); ASTOBJ_UNREF(rtppeer, dir_rtp_rtppeer_destroy); } cat = ast_category_browse(cfg, cat); } return 1; } /*! \brief Load module into PBX, register channel */ static int load_module(void) { ASTOBJ_CONTAINER_INIT(&dir_rtp_list); if (!dir_rtp_load_config()) { ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", DIR_RTP_CONFIG); return AST_MODULE_LOAD_DECLINE; } sched = sched_context_create(); if (!sched) ast_log(LOG_WARNING, "Unable to create schedule context\n"); io = io_context_create(); if (!io) ast_log(LOG_WARNING, "Unable to create I/O context\n"); ast_sockaddr_copy(&internip, &bindaddr); if (ast_find_ourip(&internip, &bindaddr, 0)) { ast_log(LOG_WARNING, "Unable to get own IP address, Direct RTP disabled\n"); return 0; } /* Register rtp control interface */ ast_rtp_proto_register(&dir_rtp_rtp); /* Register cli control interface */ ast_cli_register_multiple(dir_rtp_cli, ARRAY_LEN(dir_rtp_cli)); /* Make sure we can register our channel type */ if (ast_channel_register(&dir_rtp_tech)) { ast_log(LOG_ERROR, "Unable to register channel class %s\n", channel_type); return -1; } return 0; } /*! \brief Reload module */ static int reload(void) { return 0; } /*! \brief Unload the Direct RTP channel driver from Asterisk */ static int unload_module(void) { struct dir_rtp_pvt *privates = NULL; ast_cli_unregister_multiple(dir_rtp_cli, ARRAY_LEN(dir_rtp_cli)); /* First, take us out of the channel loop */ ast_channel_unregister(&dir_rtp_tech); ast_rtp_proto_unregister(&dir_rtp_rtp); if (!ast_mutex_lock(&dir_rtplock)) { /* Hangup all interfaces if they have an owner */ ASTOBJ_CONTAINER_TRAVERSE(&dir_rtp_list, 1, { ASTOBJ_WRLOCK(iterator); privates = iterator->p; while(privates) { if (privates->owner) ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD); privates = privates->next; } iterator->p = NULL; ASTOBJ_UNLOCK(iterator); }); ast_mutex_unlock(&dir_rtplock); } else { ast_log(LOG_WARNING, "Unable to lock the monitor\n"); return -1; } ASTOBJ_CONTAINER_DESTROYALL(&dir_rtp_list, dir_rtp_rtppeer_destroy); ASTOBJ_CONTAINER_DESTROY(&dir_rtp_list); return 0; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Direct RTP Channel Driver", .load = load_module, .unload = unload_module, .reload = reload, );