mirror of https://github.com/asterisk/asterisk
pre-release status. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@101271 65c4cc65-6c06-0410-ace0-fbb531ad65f31.6.0
parent
0d9a551f30
commit
a1bf177286
@ -1,552 +0,0 @@
|
|||||||
/*
|
|
||||||
* Asterisk -- An open source telephony toolkit.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007, Andreas 'MacBrody' Brodmann
|
|
||||||
*
|
|
||||||
* Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
|
|
||||||
*
|
|
||||||
* Information on how multicast paging works with linksys
|
|
||||||
* phones was used from FreeSWITCH's mod_esf with permission
|
|
||||||
* from Brian West.
|
|
||||||
*
|
|
||||||
* See http://www.asterisk.org for more information about
|
|
||||||
* the Asterisk project. Please do not directly contact
|
|
||||||
* any of the maintainers of this project for assistance;
|
|
||||||
* the project provides a web site, mailing lists and IRC
|
|
||||||
* channels for your use.
|
|
||||||
*
|
|
||||||
* This program is free software, distributed under the terms of
|
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
|
||||||
* at the top of the source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \file
|
|
||||||
*
|
|
||||||
* \brief Application to stream a channel's input to a specified uni-/multicast address
|
|
||||||
*
|
|
||||||
* \author Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
|
|
||||||
*
|
|
||||||
* \ingroup applications
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*** MODULEINFO
|
|
||||||
<defaultenabled>yes</defaultenabled>
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include "asterisk.h"
|
|
||||||
|
|
||||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "asterisk/file.h"
|
|
||||||
#include "asterisk/logger.h"
|
|
||||||
#include "asterisk/channel.h"
|
|
||||||
#include "asterisk/pbx.h"
|
|
||||||
#include "asterisk/module.h"
|
|
||||||
#include "asterisk/lock.h"
|
|
||||||
#include "asterisk/app.h"
|
|
||||||
#include "asterisk/config.h"
|
|
||||||
#include "asterisk/acl.h"
|
|
||||||
|
|
||||||
#define RTP_PT_ULAW 0
|
|
||||||
#define RTP_PT_GSM 3
|
|
||||||
#define RTP_PT_ALAW 8
|
|
||||||
#define RTP_PT_G729 18
|
|
||||||
|
|
||||||
/*! \brief Multicast Group Receiver Type object */
|
|
||||||
enum grouptype {
|
|
||||||
MGT_BASIC = 1, /*!< simple multicast enabled client/receiver like snom, barix */
|
|
||||||
MGT_LINKSYS = 2, /*!< linksys ipphones; they need a start/stop packet */
|
|
||||||
MGT_CISCO = 3 /*!< cisco phones; they need a http request to their internal web server // NOT YET IMPLEMENTED */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! \brief Multicast Group object */
|
|
||||||
struct mcast_group {
|
|
||||||
char name[32]; /*!< name of the group */
|
|
||||||
enum grouptype type; /*!< type, see grouptype */
|
|
||||||
int socket; /*!< socket used for streaming to this group (each group has got its own socket */
|
|
||||||
int ttl; /*!< timetolive to be set on this socket */
|
|
||||||
struct sockaddr_in rtp_address; /*!< address/port pair where the traffic is sent to */
|
|
||||||
struct sockaddr_in control_address; /*!< address/port for linksys phones to send the start/stop packet to */
|
|
||||||
AST_LIST_ENTRY(mcast_group) list; /*!< next element int group list */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! \brief RTP header object */
|
|
||||||
struct rtp_header {
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t seqno;
|
|
||||||
uint32_t timestamp;
|
|
||||||
uint32_t ssrc;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! \brief Control Packet object as used for linksys phones for start/stop packets */
|
|
||||||
struct control_packet {
|
|
||||||
uint32_t unique_id; /*!< unique id per command start or stop - not the same for both commands */
|
|
||||||
uint32_t command; /*!< the command: 6=start, 7=stop */
|
|
||||||
uint32_t ip; /*!< multicast address in network byte order */
|
|
||||||
uint32_t port; /*!< udp port to send the data to */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! \brief List to hold all the multicast groups defined in the config file */
|
|
||||||
static AST_LIST_HEAD_STATIC(groups, mcast_group);
|
|
||||||
|
|
||||||
static char *app = "RTPPage";
|
|
||||||
static char *synopsis = "RTPPage Application";
|
|
||||||
static char *descrip = " RTPPage(direct|multicast, ip:port[&ip:port]|group[&group2[&group3...]][,codec]): Sends the channel's input to the\n"
|
|
||||||
"specified group(s) defined in the config file rtppage.conf.\n"
|
|
||||||
"The optional codec may be one of the following:\n"
|
|
||||||
" ulaw - default\n"
|
|
||||||
" alaw\n"
|
|
||||||
" gsm\n"
|
|
||||||
" g729\n"
|
|
||||||
"as long as asterisk does not have to translate or respective translators are\n"
|
|
||||||
"installed with your asterisk installation. If none or any other codec is\n"
|
|
||||||
"specified the application will fall back to ulaw.\n";
|
|
||||||
|
|
||||||
static const char config[] = "rtppage.conf";
|
|
||||||
static int default_ttl = -1;
|
|
||||||
static unsigned int tos = -1;
|
|
||||||
|
|
||||||
/*! \brief Read input from channel and send it to the specified group(s) as rtp traffic */
|
|
||||||
static int rtppage_exec(struct ast_channel *chan, void *data)
|
|
||||||
{
|
|
||||||
int res = 0;
|
|
||||||
struct ast_module_user *u = NULL;
|
|
||||||
struct ast_frame *f = NULL;
|
|
||||||
char *parse = NULL;
|
|
||||||
char *rest = NULL, *cur = NULL;
|
|
||||||
char *rest2 = NULL;
|
|
||||||
char *ip = NULL, *port = NULL;
|
|
||||||
int ms = -1;
|
|
||||||
unsigned char *databuf = NULL;
|
|
||||||
struct sockaddr_in destaddr;
|
|
||||||
struct mcast_group *group;
|
|
||||||
struct control_packet cpk;
|
|
||||||
struct rtp_header *rtph = NULL;
|
|
||||||
uint8_t rtp_pt = RTP_PT_ULAW;
|
|
||||||
int chan_format = AST_FORMAT_ULAW;
|
|
||||||
uint16_t rtpflags = 0;
|
|
||||||
int ttl = 0;
|
|
||||||
int pagetype = 0;
|
|
||||||
/* you can specify three arguments:
|
|
||||||
* 1) pagetype (0 = direct, 1 = multicast)
|
|
||||||
* 2) groups, e.g. NameOfGroup or Name1&Name2 etc) / or ip:port in case of direct
|
|
||||||
* 3) optional: codec, if specified and valid
|
|
||||||
* this codec will be used for streaming
|
|
||||||
*/
|
|
||||||
AST_DECLARE_APP_ARGS(args,
|
|
||||||
AST_APP_ARG(pagetype);
|
|
||||||
AST_APP_ARG(groups);
|
|
||||||
AST_APP_ARG(codec);
|
|
||||||
);
|
|
||||||
AST_LIST_HEAD(, mcast_group) activegroups;
|
|
||||||
|
|
||||||
AST_LIST_HEAD_INIT(&activegroups);
|
|
||||||
|
|
||||||
/* make sure there is at least one parameter */
|
|
||||||
if (ast_strlen_zero(data)) {
|
|
||||||
ast_log(LOG_WARNING, "%s requires argument (group(s)[,codec])\n", app);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse = ast_strdupa(data);
|
|
||||||
AST_STANDARD_APP_ARGS(args, parse);
|
|
||||||
|
|
||||||
/* pagetype is a mandatory parameter */
|
|
||||||
if (!args.pagetype) {
|
|
||||||
ast_log(LOG_WARNING, "%s requires arguments (pagetype, group(s) | ip:port[,codec])\n", app);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if (!strcasecmp(args.pagetype, "direct")) {
|
|
||||||
pagetype = 0;
|
|
||||||
} else if (!strcasecmp(args.pagetype, "multicast")) {
|
|
||||||
pagetype = 1;
|
|
||||||
} else {
|
|
||||||
ast_log(LOG_ERROR, "%s is an invalid grouptype! valid types are: direct, multicast.\n", args.pagetype);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* group is a mandatory parameter */
|
|
||||||
if (!args.groups) {
|
|
||||||
ast_log(LOG_WARNING, "%s requires arguments (pagetype, group(s) | ip:port[,codec])\n", app);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup variables for the desired codec */
|
|
||||||
if (args.codec) {
|
|
||||||
if (!strcasecmp(args.codec, "ulaw")) {
|
|
||||||
/* use default settings */
|
|
||||||
} else if (!strcasecmp(args.codec, "alaw")) {
|
|
||||||
rtp_pt = RTP_PT_ALAW;
|
|
||||||
chan_format = AST_FORMAT_ALAW;
|
|
||||||
} else if (!strcasecmp(args.codec, "gsm")) {
|
|
||||||
rtp_pt = RTP_PT_GSM;
|
|
||||||
chan_format = AST_FORMAT_GSM;
|
|
||||||
} else if (!strcasecmp(args.codec, "g729")) {
|
|
||||||
rtp_pt = RTP_PT_G729;
|
|
||||||
chan_format = AST_FORMAT_G729A;
|
|
||||||
} else {
|
|
||||||
/* use ulaw as fallback */
|
|
||||||
rtp_pt = RTP_PT_ULAW;
|
|
||||||
chan_format = AST_FORMAT_ULAW;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u = ast_module_user_add(chan);
|
|
||||||
|
|
||||||
/* Check if the channel is answered, if not
|
|
||||||
* do answer it */
|
|
||||||
if (chan->_state != AST_STATE_UP) {
|
|
||||||
res = ast_answer(chan);
|
|
||||||
if (res) {
|
|
||||||
ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate memory for the rtp send buffer */
|
|
||||||
if ((databuf = ast_calloc(1, 172)) == NULL) {
|
|
||||||
ast_log(LOG_WARNING, "Failed to allocate memory for the data buffer, give up\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize rtp buffer header
|
|
||||||
* with rtp version and
|
|
||||||
* payload type
|
|
||||||
*/
|
|
||||||
rtph = (struct rtp_header *)databuf;
|
|
||||||
rtpflags = (0x02 << 14); /* rtp v2 */
|
|
||||||
rtpflags = (rtpflags & 0xFF80) | rtp_pt;
|
|
||||||
rtph->flags = htons(rtpflags);
|
|
||||||
rtph->ssrc = htonl((u_long)time(NULL));
|
|
||||||
|
|
||||||
/* first create a temporary table for this page session
|
|
||||||
* containing all groups which will be used
|
|
||||||
*/
|
|
||||||
AST_LIST_LOCK(&groups);
|
|
||||||
rest = ast_strdup(args.groups);
|
|
||||||
if (pagetype == 0) {
|
|
||||||
/* a direct page call. this can actually be used
|
|
||||||
* for multicast paging too by passing the ip:port as
|
|
||||||
* argument 2
|
|
||||||
*/
|
|
||||||
while ((cur = strsep(&rest, "&"))) {
|
|
||||||
struct mcast_group *agroup = ast_calloc(1, sizeof(*agroup));
|
|
||||||
rest2 = ast_strdup(cur);
|
|
||||||
ip = strsep(&rest2, ":");
|
|
||||||
port = strsep(&rest2, ":");
|
|
||||||
if (ip == NULL || port == NULL) {
|
|
||||||
ast_log(LOG_WARNING, "invalid ip:port pair in call to RTPPage (%s)!\n", cur);
|
|
||||||
free(agroup);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
agroup->rtp_address.sin_family = AF_INET;
|
|
||||||
agroup->rtp_address.sin_port = htons(atoi(port));
|
|
||||||
if (inet_pton(AF_INET, ip, &agroup->rtp_address.sin_addr) <= 0) {
|
|
||||||
ast_log(LOG_WARNING, "invalid ip in call to RTPPage (%s)!\n", cur);
|
|
||||||
free(agroup);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
agroup->type = MGT_BASIC;
|
|
||||||
agroup->socket = -1;
|
|
||||||
agroup->ttl = -1;
|
|
||||||
AST_LIST_INSERT_TAIL(&activegroups, agroup, list);
|
|
||||||
}
|
|
||||||
} else if (pagetype == 1) {
|
|
||||||
/* a multicast page call */
|
|
||||||
while ((cur = strsep(&rest, "&"))) {
|
|
||||||
AST_LIST_TRAVERSE(&groups, group, list) {
|
|
||||||
if (!strcasecmp(group->name, cur)) {
|
|
||||||
struct mcast_group *agroup = ast_calloc(1, sizeof(*agroup));
|
|
||||||
memcpy(agroup->name, group->name, 32);
|
|
||||||
agroup->type = group->type;
|
|
||||||
agroup->socket = group->socket;
|
|
||||||
agroup->ttl = group->ttl;
|
|
||||||
memcpy(&agroup->rtp_address, &group->rtp_address, sizeof(agroup->rtp_address));
|
|
||||||
memcpy(&agroup->control_address, &group->control_address, sizeof(agroup->control_address));
|
|
||||||
AST_LIST_INSERT_TAIL(&activegroups, agroup, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AST_LIST_UNLOCK(&groups);
|
|
||||||
|
|
||||||
/* now initialize these groups, e.g. create a udp socket for each,
|
|
||||||
* set ttl and tos if requested by config, and
|
|
||||||
* in case of linksys type groups send the multicast start signal
|
|
||||||
*/
|
|
||||||
AST_LIST_TRAVERSE(&activegroups, group, list) {
|
|
||||||
group->socket = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
/* set ttl if configured
|
|
||||||
* ttl can be configured either globally in the
|
|
||||||
* category 'general' or locally within
|
|
||||||
* the respective groups
|
|
||||||
*/
|
|
||||||
if (group->ttl >= 0 || default_ttl >= 0) {
|
|
||||||
ttl = default_ttl;
|
|
||||||
if (group->ttl >= 0) {
|
|
||||||
ttl = group->ttl;
|
|
||||||
}
|
|
||||||
if (setsockopt(group->socket, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
|
|
||||||
ast_log(LOG_WARNING, "Failed to set ttl on socket for group %s!\n", group->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* set tos if requested
|
|
||||||
* tos can only be configured globally ('general')
|
|
||||||
*/
|
|
||||||
if (tos >= 0) {
|
|
||||||
if (setsockopt(group->socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
|
|
||||||
ast_log(LOG_WARNING, "Failed to set tos field on socket for group %s!\n", group->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* for linksys device groups send multicast start command */
|
|
||||||
if (group->type == MGT_LINKSYS) {
|
|
||||||
cpk.unique_id = htonl((u_long)time(NULL));
|
|
||||||
cpk.command = htonl(6); /* multicast start command */
|
|
||||||
memcpy(&cpk.ip, &group->rtp_address.sin_addr, sizeof(cpk.ip));
|
|
||||||
cpk.port = htonl(ntohs(group->rtp_address.sin_port));
|
|
||||||
memcpy(&destaddr, &group->control_address, sizeof(destaddr));
|
|
||||||
sendto(group->socket, &cpk, sizeof(cpk), 0, (struct sockaddr *)&destaddr, sizeof(destaddr));
|
|
||||||
sendto(group->socket, &cpk, sizeof(cpk), 0, (struct sockaddr *)&destaddr, sizeof(destaddr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set read format as configured - this codec will be used for streaming */
|
|
||||||
res = ast_set_read_format(chan, chan_format);
|
|
||||||
if (res < 0) {
|
|
||||||
ast_log(LOG_WARNING, "Unable to set channel read mode, giving up\n");
|
|
||||||
res = -1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Play a beep to let the caller know he can start talking */
|
|
||||||
res = ast_streamfile(chan, "beep", chan->language);
|
|
||||||
if (!res) {
|
|
||||||
res = ast_waitstream(chan, "");
|
|
||||||
} else {
|
|
||||||
ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
|
|
||||||
}
|
|
||||||
ast_stopstream(chan);
|
|
||||||
|
|
||||||
/* main loop:
|
|
||||||
* read frames from the input channel and, if they are voice frames,
|
|
||||||
* send them to all requested multi-/unicast listeners.
|
|
||||||
*/
|
|
||||||
for (;;) {
|
|
||||||
ms = ast_waitfor(chan, 1000);
|
|
||||||
if (ms < 0) {
|
|
||||||
ast_log(LOG_DEBUG, "Hangup detected\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
f = ast_read(chan);
|
|
||||||
if (!f)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* if the speaker pressed '#', then quit */
|
|
||||||
if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
|
|
||||||
res = 0;
|
|
||||||
ast_frfree(f);
|
|
||||||
ast_log(LOG_DEBUG, "Received DTMF key: %d\n", f->subclass);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f->frametype == AST_FRAME_VOICE) {
|
|
||||||
/* update the rtp header */
|
|
||||||
rtph = (struct rtp_header *)databuf;
|
|
||||||
rtph->seqno = htons(f->seqno);
|
|
||||||
rtph->timestamp = htonl(f->ts * 8);
|
|
||||||
memcpy(databuf+12, f->data, f->datalen);
|
|
||||||
|
|
||||||
/* now send that frame to the destination groups */
|
|
||||||
AST_LIST_TRAVERSE(&activegroups, group, list) {
|
|
||||||
memcpy(&destaddr, &group->rtp_address, sizeof(destaddr));
|
|
||||||
if (sendto(group->socket, databuf, f->datalen+12, 0, (struct sockaddr *)&destaddr, sizeof(destaddr)) <= 0) {
|
|
||||||
ast_log(LOG_DEBUG, "sendto() failed!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast_frfree(f);
|
|
||||||
f = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
|
|
||||||
/* send a stop multicast signal to all linksys devices */
|
|
||||||
AST_LIST_TRAVERSE(&activegroups, group, list) {
|
|
||||||
if (group->socket > 0) {
|
|
||||||
if (group->type == MGT_LINKSYS) {
|
|
||||||
cpk.unique_id = htonl((u_long)time(NULL));
|
|
||||||
cpk.command = htonl(7); /* multicast stop command */
|
|
||||||
memcpy(&cpk.ip, &group->rtp_address.sin_addr, sizeof(cpk.ip));
|
|
||||||
cpk.port = htonl(ntohs(group->rtp_address.sin_port));
|
|
||||||
memcpy(&destaddr, &group->control_address, sizeof(destaddr));
|
|
||||||
sendto(group->socket, &cpk, 8, 0, (struct sockaddr *)&destaddr, sizeof(destaddr));
|
|
||||||
sendto(group->socket, &cpk, 8, 0, (struct sockaddr *)&destaddr, sizeof(destaddr));
|
|
||||||
}
|
|
||||||
close(group->socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free activegroups list */
|
|
||||||
while ((group = AST_LIST_REMOVE_HEAD(&activegroups, list))) {
|
|
||||||
free(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free the rtp data buffer */
|
|
||||||
if (databuf != NULL) {
|
|
||||||
free(databuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_module_user_remove(u);
|
|
||||||
ast_log(LOG_DEBUG, "Exit RTPPage(%s)\n", args.groups);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int load_config(int reload) {
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
const char *cat = NULL;
|
|
||||||
struct ast_config *cfg = NULL;
|
|
||||||
struct mcast_group *group = NULL;
|
|
||||||
const char *var = NULL;
|
|
||||||
struct ast_flags config_flags = { 0 };
|
|
||||||
|
|
||||||
AST_LIST_LOCK(&groups);
|
|
||||||
if (reload) {
|
|
||||||
/* if this is a reload, then free the config structure before
|
|
||||||
* filling it again
|
|
||||||
*/
|
|
||||||
while ((group = AST_LIST_REMOVE_HEAD(&groups, list))) {
|
|
||||||
free(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset default_ttl & tos */
|
|
||||||
default_ttl = -1; /* means not set */
|
|
||||||
tos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* load config file */
|
|
||||||
if (!(cfg = ast_config_load(config, config_flags))) {
|
|
||||||
ast_log(LOG_NOTICE, "Failed to load config!\n");
|
|
||||||
AST_LIST_UNLOCK(&groups);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((cat = ast_category_browse(cfg, cat)) != NULL) {
|
|
||||||
/* 'general' is reserved for generic options */
|
|
||||||
if (!strcasecmp(cat, "general")) {
|
|
||||||
var = ast_variable_retrieve(cfg, cat, "ttl");
|
|
||||||
if (var) {
|
|
||||||
default_ttl = atoi(var);
|
|
||||||
}
|
|
||||||
var = ast_variable_retrieve(cfg, cat, "tos");
|
|
||||||
if (var) {
|
|
||||||
ast_str2tos(var, &tos);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
group = ast_calloc(1, sizeof(*group));
|
|
||||||
var = ast_variable_retrieve(cfg, cat, "type");
|
|
||||||
if (!strcasecmp(var, "basic")) {
|
|
||||||
ast_copy_string(group->name, cat, sizeof(group->name));
|
|
||||||
group->type = MGT_BASIC;
|
|
||||||
group->socket = -1;
|
|
||||||
group->ttl = -1;
|
|
||||||
if (ast_variable_retrieve(cfg, cat, "ttl") != NULL) {
|
|
||||||
group->ttl = atoi(ast_variable_retrieve(cfg, cat, "ttl"));
|
|
||||||
}
|
|
||||||
memset(&group->rtp_address, 0, sizeof(group->rtp_address));
|
|
||||||
group->rtp_address.sin_family = AF_INET;
|
|
||||||
group->rtp_address.sin_port = htons(atoi(ast_variable_retrieve(cfg, cat, "rtp_port")));
|
|
||||||
if (inet_pton(AF_INET, ast_variable_retrieve(cfg, cat, "rtp_address"), &group->rtp_address.sin_addr) <= 0) {
|
|
||||||
ast_log(LOG_NOTICE, "Invalid ip address in group %s!\n", cat);
|
|
||||||
ast_free(group);
|
|
||||||
group = NULL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (!strcasecmp(var, "linksys")) {
|
|
||||||
ast_copy_string(group->name, cat, sizeof(group->name));
|
|
||||||
group->type = MGT_LINKSYS;
|
|
||||||
group->socket = -1;
|
|
||||||
group->ttl = -1;
|
|
||||||
if (ast_variable_retrieve(cfg, cat, "ttl") != NULL) {
|
|
||||||
group->ttl = atoi(ast_variable_retrieve(cfg, cat, "ttl"));
|
|
||||||
}
|
|
||||||
memset(&group->rtp_address, 0, sizeof(group->rtp_address));
|
|
||||||
group->rtp_address.sin_family = AF_INET;
|
|
||||||
group->rtp_address.sin_port = htons(atoi(ast_variable_retrieve(cfg, cat, "rtp_port")));
|
|
||||||
if (inet_pton(AF_INET, ast_variable_retrieve(cfg, cat, "rtp_address"), &group->rtp_address.sin_addr) <= 0) {
|
|
||||||
ast_log(LOG_NOTICE, "Invalid ip address in group %s!\n", cat);
|
|
||||||
ast_free(group);
|
|
||||||
group = NULL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
memset(&group->control_address, 0, sizeof(group->control_address));
|
|
||||||
group->control_address.sin_family = AF_INET;
|
|
||||||
group->control_address.sin_port = htons(atoi(ast_variable_retrieve(cfg, cat, "control_port")));
|
|
||||||
if (inet_pton(AF_INET, ast_variable_retrieve(cfg, cat, "control_address"), &group->control_address.sin_addr) <= 0) {
|
|
||||||
ast_log(LOG_NOTICE, "Invalid ip address in group %s!\n", cat);
|
|
||||||
ast_free(group);
|
|
||||||
group = NULL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
group->type = -1;
|
|
||||||
group->socket = -1;
|
|
||||||
group->ttl = -1;
|
|
||||||
ast_log(LOG_NOTICE, "Invalid mcast group %s!\n", cat);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now add it to the linked list */
|
|
||||||
AST_LIST_INSERT_TAIL(&groups, group, list);
|
|
||||||
ast_log(LOG_NOTICE, "loaded category %s\n", group->name);
|
|
||||||
group = NULL;
|
|
||||||
var = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
AST_LIST_UNLOCK(&groups);
|
|
||||||
|
|
||||||
ast_config_destroy(cfg);
|
|
||||||
|
|
||||||
return(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int unload_module(void)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
res = ast_unregister_application(app);
|
|
||||||
ast_module_user_hangup_all();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int load_module(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
load_config(0);
|
|
||||||
return ast_register_application(app, rtppage_exec, synopsis, descrip);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int reload(void)
|
|
||||||
{
|
|
||||||
return load_config(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RTPPage Application",
|
|
||||||
.load = load_module,
|
|
||||||
.unload = unload_module,
|
|
||||||
.reload = reload,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
; Configuration for the rtppage() application
|
|
||||||
; that sends audio in multicast or unicast mode to phones
|
|
||||||
; for paging
|
|
||||||
|
|
||||||
[general]
|
|
||||||
ttl=10
|
|
||||||
tos=ef
|
|
||||||
|
|
||||||
[testgroup]
|
|
||||||
type=basic
|
|
||||||
rtp_address=192.168.83.147
|
|
||||||
rtp_port=12346
|
|
||||||
|
|
||||||
[linksysgroup]
|
|
||||||
type=linksys
|
|
||||||
rtp_address=224.168.168.168
|
|
||||||
rtp_port=34567
|
|
||||||
control_address=224.168.168.168
|
|
||||||
control_port=6061
|
|
||||||
|
|
Loading…
Reference in new issue