Skip to content
Snippets Groups Projects
Commit ae87ba45 authored by Joshua Colp's avatar Joshua Colp
Browse files

Add support for multicast RTP paging.

(closes issue #11797)
Reported by: macbrody

Review: https://reviewboard.asterisk.org/r/270/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@203227 65c4cc65-6c06-0410-ace0-fbb531ad65f3
parent ca3a181c
No related branches found
No related tags found
No related merge requests found
......@@ -173,6 +173,16 @@ Calendaring for Asterisk
iCalendar, CalDAV, and Exchange Server calendars are supported (Exchange support
only tested on Exchange Server 2003 with no support for forms-based authentication).
Multicast RTP Support
---------------------
* A new RTP engine and channel driver have been added which supports Multicast RTP.
The channel driver can be used with the Page application to perform multicast RTP
paging. The dial string format is: MulticastRTP/<type>/<destination>/<control address>
Type can be either basic or linksys.
Destination is the IP address and port for the RTP packets.
Control address is specific to the linksys type and is used for sending the control
packets unique to them.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 1.6.1 to Asterisk 1.6.2 -------------
------------------------------------------------------------------------------
......
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2009, Digium, Inc.
*
* Joshua Colp <jcolp@digium.com>
* Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
*
* 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
*
* \author Joshua Colp <jcolp@digium.com>
* \author Andreas 'MacBrody' Broadmann <andreas.brodmann@gmail.com>
*
* \brief Multicast RTP Paging Channel
*
* \ingroup channel_drivers
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <fcntl.h>
#include <sys/signal.h>
#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/acl.h"
#include "asterisk/callerid.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/causes.h"
static const char tdesc[] = "Multicast RTP Paging Channel Driver";
/* Forward declarations */
static struct ast_channel *multicast_rtp_request(const char *type, int format, void *data, int *cause);
static int multicast_rtp_call(struct ast_channel *ast, char *dest, int timeout);
static int multicast_rtp_hangup(struct ast_channel *ast);
static struct ast_frame *multicast_rtp_read(struct ast_channel *ast);
static int multicast_rtp_write(struct ast_channel *ast, struct ast_frame *f);
/* Channel driver declaration */
static const struct ast_channel_tech multicast_rtp_tech = {
.type = "MulticastRTP",
.description = tdesc,
.capabilities = -1,
.requester = multicast_rtp_request,
.call = multicast_rtp_call,
.hangup = multicast_rtp_hangup,
.read = multicast_rtp_read,
.write = multicast_rtp_write,
};
/*! \brief Function called when we should read a frame from the channel */
static struct ast_frame *multicast_rtp_read(struct ast_channel *ast)
{
return &ast_null_frame;
}
/*! \brief Function called when we should write a frame to the channel */
static int multicast_rtp_write(struct ast_channel *ast, struct ast_frame *f)
{
struct ast_rtp_instance *instance = ast->tech_pvt;
return ast_rtp_instance_write(instance, f);
}
/*! \brief Function called when we should actually call the destination */
static int multicast_rtp_call(struct ast_channel *ast, char *dest, int timeout)
{
struct ast_rtp_instance *instance = ast->tech_pvt;
ast_queue_control(ast, AST_CONTROL_ANSWER);
return ast_rtp_instance_activate(instance);
}
/*! \brief Function called when we should hang the channel up */
static int multicast_rtp_hangup(struct ast_channel *ast)
{
struct ast_rtp_instance *instance = ast->tech_pvt;
ast_rtp_instance_destroy(instance);
ast->tech_pvt = NULL;
return 0;
}
/*! \brief Function called when we should prepare to call the destination */
static struct ast_channel *multicast_rtp_request(const char *type, int format, void *data, int *cause)
{
char *tmp = ast_strdupa(data), *multicast_type = tmp, *destination, *control;
struct ast_rtp_instance *instance;
struct sockaddr_in control_address = { .sin_family = AF_INET, }, destination_address = { .sin_family = AF_INET, };
struct ast_channel *chan;
int fmt = ast_best_codec(format);
/* If no type was given we can't do anything */
if (ast_strlen_zero(multicast_type)) {
goto failure;
}
if (!(destination = strchr(tmp, '/'))) {
goto failure;
}
*destination++ = '\0';
if (ast_parse_arg(destination, PARSE_INADDR | PARSE_PORT_REQUIRE, &destination_address)) {
goto failure;
}
if ((control = strchr(destination, '/'))) {
*control++ = '\0';
if (ast_parse_arg(control, PARSE_INADDR | PARSE_PORT_REQUIRE, &control_address)) {
goto failure;
}
}
if (!(instance = ast_rtp_instance_new("multicast", NULL, &control_address, multicast_type))) {
goto failure;
}
if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", 0, "MulticastRTP/%p", instance))) {
ast_rtp_instance_destroy(instance);
goto failure;
}
ast_rtp_instance_set_remote_address(instance, &destination_address);
chan->tech = &multicast_rtp_tech;
chan->nativeformats = fmt;
chan->writeformat = fmt;
chan->readformat = fmt;
chan->rawwriteformat = fmt;
chan->rawreadformat = fmt;
chan->tech_pvt = instance;
return chan;
failure:
*cause = AST_CAUSE_FAILURE;
return NULL;
}
/*! \brief Function called when our module is loaded */
static int load_module(void)
{
if (ast_channel_register(&multicast_rtp_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class 'MulticastRTP'\n");
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
/*! \brief Function called when our module is unloaded */
static int unload_module(void)
{
ast_channel_unregister(&multicast_rtp_tech);
return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Multicast RTP Paging Channel");
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2009, Digium, Inc.
*
* Joshua Colp <jcolp@digium.com>
* Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
*
* 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 Multicast RTP Engine
*
* \author Joshua Colp <jcolp@digium.com>
* \author Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <math.h>
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/channel.h"
#include "asterisk/acl.h"
#include "asterisk/config.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/netsock.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/unaligned.h"
#include "asterisk/module.h"
#include "asterisk/rtp_engine.h"
/*! Command value used for Linksys paging to indicate we are starting */
#define LINKSYS_MCAST_STARTCMD 6
/*! Command value used for Linksys paging to indicate we are stopping */
#define LINKSYS_MCAST_STOPCMD 7
/*! \brief Type of paging to do */
enum multicast_type {
/*! Simple multicast enabled client/receiver paging like Snom and Barix uses */
MULTICAST_TYPE_BASIC = 0,
/*! More advanced Linksys type paging which requires a start and stop packet */
MULTICAST_TYPE_LINKSYS,
};
/*! \brief Structure for a Linksys control packet */
struct multicast_control_packet {
/*! Unique identifier for the control packet */
uint32_t unique_id;
/*! Actual command in the control packet */
uint32_t command;
/*! IP address for the RTP */
uint32_t ip;
/*! Port for the RTP */
uint32_t port;
};
/*! \brief Structure for a multicast paging instance */
struct multicast_rtp {
/*! TYpe of multicast paging this instance is doing */
enum multicast_type type;
/*! Socket used for sending the audio on */
int socket;
/*! Synchronization source value, used when creating/sending the RTP packet */
unsigned int ssrc;
/*! Sequence number, used when creating/sending the RTP packet */
unsigned int seqno;
};
/* Forward Declarations */
static int multicast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data);
static int multicast_rtp_activate(struct ast_rtp_instance *instance);
static int multicast_rtp_destroy(struct ast_rtp_instance *instance);
static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame);
static struct ast_frame *multicast_rtp_read(struct ast_rtp_instance *instance, int rtcp);
/* RTP Engine Declaration */
static struct ast_rtp_engine multicast_rtp_engine = {
.name = "multicast",
.new = multicast_rtp_new,
.activate = multicast_rtp_activate,
.destroy = multicast_rtp_destroy,
.write = multicast_rtp_write,
.read = multicast_rtp_read,
};
/*! \brief Function called to create a new multicast instance */
static int multicast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data)
{
struct multicast_rtp *multicast;
const char *type = data;
if (!(multicast = ast_calloc(1, sizeof(*multicast)))) {
return -1;
}
if (!strcasecmp(type, "basic")) {
multicast->type = MULTICAST_TYPE_BASIC;
} else if (!strcasecmp(type, "linksys")) {
multicast->type = MULTICAST_TYPE_LINKSYS;
} else {
ast_free(multicast);
return -1;
}
if ((multicast->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
ast_free(multicast);
return -1;
}
multicast->ssrc = ast_random();
ast_rtp_instance_set_data(instance, multicast);
return 0;
}
/*! \brief Helper function which populates a control packet with useful information and sends it */
static int multicast_send_control_packet(struct ast_rtp_instance *instance, struct multicast_rtp *multicast, int command)
{
struct multicast_control_packet control_packet = { .unique_id = htonl((u_long)time(NULL)),
.command = htonl(command),
};
struct sockaddr_in control_address, remote_address;
ast_rtp_instance_get_local_address(instance, &control_address);
ast_rtp_instance_get_remote_address(instance, &remote_address);
/* Ensure the user of us have given us both the control address and destination address */
if (!control_address.sin_addr.s_addr || !remote_address.sin_addr.s_addr) {
return -1;
}
control_packet.ip = remote_address.sin_addr.s_addr;
control_packet.port = htonl(ntohs(remote_address.sin_port));
/* Based on a recommendation by Brian West who did the FreeSWITCH implementation we send control packets twice */
sendto(multicast->socket, &control_packet, sizeof(control_packet), 0, (struct sockaddr *)&control_address, sizeof(control_address));
sendto(multicast->socket, &control_packet, sizeof(control_packet), 0, (struct sockaddr *)&control_address, sizeof(control_address));
return 0;
}
/*! \brief Function called to indicate that audio is now going to flow */
static int multicast_rtp_activate(struct ast_rtp_instance *instance)
{
struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
if (multicast->type != MULTICAST_TYPE_LINKSYS) {
return 0;
}
return multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STARTCMD);
}
/*! \brief Function called to destroy a multicast instance */
static int multicast_rtp_destroy(struct ast_rtp_instance *instance)
{
struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
if (multicast->type == MULTICAST_TYPE_LINKSYS) {
multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STOPCMD);
}
close(multicast->socket);
ast_free(multicast);
return 0;
}
/*! \brief Function called to broadcast some audio on a multicast instance */
static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
{
struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
struct ast_frame *f = frame;
struct sockaddr_in remote_address;
int hdrlen = 12, res, codec;
unsigned char *rtpheader;
/* We only accept audio, nothing else */
if (frame->frametype != AST_FRAME_VOICE) {
return 0;
}
/* Grab the actual payload number for when we create the RTP packet */
if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, frame->subclass)) < 0) {
return -1;
}
/* If we do not have space to construct an RTP header duplicate the frame so we get some */
if (frame->offset < hdrlen) {
f = ast_frdup(frame);
}
/* Construct an RTP header for our packet */
rtpheader = (unsigned char *)(f->data.ptr - hdrlen);
put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (multicast->seqno++) | (0 << 23)));
put_unaligned_uint32(rtpheader + 4, htonl(f->ts * 8));
put_unaligned_uint32(rtpheader + 8, htonl(multicast->ssrc));
/* Finally send it out to the eager phones listening for us */
ast_rtp_instance_get_remote_address(instance, &remote_address);
res = sendto(multicast->socket, (void *) rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *) &remote_address, sizeof(remote_address));
if (res < 0) {
ast_log(LOG_ERROR, "Multicast RTP Transmission error to %s:%u: %s\n",
ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno));
}
/* If we were forced to duplicate the frame free the new one */
if (frame != f) {
ast_frfree(f);
}
return res;
}
/*! \brief Function called to read from a multicast instance */
static struct ast_frame *multicast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
{
return &ast_null_frame;
}
static int load_module(void)
{
if (ast_rtp_engine_register(&multicast_rtp_engine)) {
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_rtp_engine_unregister(&multicast_rtp_engine);
return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Multicast RTP Engine");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment