Wed Apr 6 11:29:47 2011

Asterisk developer's documentation


res_rtp_multicast.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  * Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  *
00023  * \brief Multicast RTP Engine
00024  *
00025  * \author Joshua Colp <jcolp@digium.com>
00026  * \author Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com>
00027  *
00028  * \ingroup rtp_engines
00029  */
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00034 
00035 #include <sys/time.h>
00036 #include <signal.h>
00037 #include <fcntl.h>
00038 #include <math.h>
00039 
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/frame.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/acl.h"
00044 #include "asterisk/config.h"
00045 #include "asterisk/lock.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/cli.h"
00048 #include "asterisk/manager.h"
00049 #include "asterisk/unaligned.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/rtp_engine.h"
00052 
00053 /*! Command value used for Linksys paging to indicate we are starting */
00054 #define LINKSYS_MCAST_STARTCMD 6
00055 
00056 /*! Command value used for Linksys paging to indicate we are stopping */
00057 #define LINKSYS_MCAST_STOPCMD 7
00058 
00059 /*! \brief Type of paging to do */
00060 enum multicast_type {
00061    /*! Simple multicast enabled client/receiver paging like Snom and Barix uses */
00062    MULTICAST_TYPE_BASIC = 0,
00063    /*! More advanced Linksys type paging which requires a start and stop packet */
00064    MULTICAST_TYPE_LINKSYS,
00065 };
00066 
00067 /*! \brief Structure for a Linksys control packet */
00068 struct multicast_control_packet {
00069    /*! Unique identifier for the control packet */
00070    uint32_t unique_id;
00071    /*! Actual command in the control packet */
00072    uint32_t command;
00073    /*! IP address for the RTP */
00074    uint32_t ip;
00075    /*! Port for the RTP */
00076    uint32_t port;
00077 };
00078 
00079 /*! \brief Structure for a multicast paging instance */
00080 struct multicast_rtp {
00081    /*! TYpe of multicast paging this instance is doing */
00082    enum multicast_type type;
00083    /*! Socket used for sending the audio on */
00084    int socket;
00085    /*! Synchronization source value, used when creating/sending the RTP packet */
00086    unsigned int ssrc;
00087    /*! Sequence number, used when creating/sending the RTP packet */
00088    unsigned int seqno;
00089 };
00090 
00091 /* Forward Declarations */
00092 static int multicast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct ast_sockaddr *addr, void *data);
00093 static int multicast_rtp_activate(struct ast_rtp_instance *instance);
00094 static int multicast_rtp_destroy(struct ast_rtp_instance *instance);
00095 static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame);
00096 static struct ast_frame *multicast_rtp_read(struct ast_rtp_instance *instance, int rtcp);
00097 
00098 /* RTP Engine Declaration */
00099 static struct ast_rtp_engine multicast_rtp_engine = {
00100    .name = "multicast",
00101    .new = multicast_rtp_new,
00102    .activate = multicast_rtp_activate,
00103    .destroy = multicast_rtp_destroy,
00104    .write = multicast_rtp_write,
00105    .read = multicast_rtp_read,
00106 };
00107 
00108 /*! \brief Function called to create a new multicast instance */
00109 static int multicast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct ast_sockaddr *addr, void *data)
00110 {
00111    struct multicast_rtp *multicast;
00112    const char *type = data;
00113 
00114    if (!(multicast = ast_calloc(1, sizeof(*multicast)))) {
00115       return -1;
00116    }
00117 
00118    if (!strcasecmp(type, "basic")) {
00119       multicast->type = MULTICAST_TYPE_BASIC;
00120    } else if (!strcasecmp(type, "linksys")) {
00121       multicast->type = MULTICAST_TYPE_LINKSYS;
00122    } else {
00123       ast_free(multicast);
00124       return -1;
00125    }
00126 
00127    if ((multicast->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
00128       ast_free(multicast);
00129       return -1;
00130    }
00131 
00132    multicast->ssrc = ast_random();
00133 
00134    ast_rtp_instance_set_data(instance, multicast);
00135 
00136    return 0;
00137 }
00138 
00139 /*! \brief Helper function which populates a control packet with useful information and sends it */
00140 static int multicast_send_control_packet(struct ast_rtp_instance *instance, struct multicast_rtp *multicast, int command)
00141 {
00142    struct multicast_control_packet control_packet = { .unique_id = htonl((u_long)time(NULL)),
00143                         .command = htonl(command),
00144    };
00145    struct ast_sockaddr control_address, remote_address;
00146 
00147    ast_rtp_instance_get_local_address(instance, &control_address);
00148    ast_rtp_instance_get_remote_address(instance, &remote_address);
00149 
00150    /* Ensure the user of us have given us both the control address and destination address */
00151    if (ast_sockaddr_isnull(&control_address) ||
00152        ast_sockaddr_isnull(&remote_address)) {
00153       return -1;
00154    }
00155 
00156    /* The protocol only supports IPv4. */
00157    if (ast_sockaddr_is_ipv6(&remote_address)) {
00158       ast_log(LOG_WARNING, "Cannot send control packet for IPv6 "
00159          "remote address.\n");
00160       return -1;
00161    }
00162 
00163    control_packet.ip = htonl(ast_sockaddr_ipv4(&remote_address));
00164    control_packet.port = htonl(ast_sockaddr_port(&remote_address));
00165 
00166    /* Based on a recommendation by Brian West who did the FreeSWITCH implementation we send control packets twice */
00167    ast_sendto(multicast->socket, &control_packet, sizeof(control_packet), 0, &control_address);
00168    ast_sendto(multicast->socket, &control_packet, sizeof(control_packet), 0, &control_address);
00169 
00170    return 0;
00171 }
00172 
00173 /*! \brief Function called to indicate that audio is now going to flow */
00174 static int multicast_rtp_activate(struct ast_rtp_instance *instance)
00175 {
00176    struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
00177 
00178    if (multicast->type != MULTICAST_TYPE_LINKSYS) {
00179       return 0;
00180    }
00181 
00182    return multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STARTCMD);
00183 }
00184 
00185 /*! \brief Function called to destroy a multicast instance */
00186 static int multicast_rtp_destroy(struct ast_rtp_instance *instance)
00187 {
00188    struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
00189 
00190    if (multicast->type == MULTICAST_TYPE_LINKSYS) {
00191       multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STOPCMD);
00192    }
00193 
00194    close(multicast->socket);
00195 
00196    ast_free(multicast);
00197 
00198    return 0;
00199 }
00200 
00201 /*! \brief Function called to broadcast some audio on a multicast instance */
00202 static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
00203 {
00204    struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
00205    struct ast_frame *f = frame;
00206    struct ast_sockaddr remote_address;
00207    int hdrlen = 12, res, codec;
00208    unsigned char *rtpheader;
00209 
00210    /* We only accept audio, nothing else */
00211    if (frame->frametype != AST_FRAME_VOICE) {
00212       return 0;
00213    }
00214 
00215    /* Grab the actual payload number for when we create the RTP packet */
00216    if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, frame->subclass.codec)) < 0) {
00217       return -1;
00218    }
00219 
00220    /* If we do not have space to construct an RTP header duplicate the frame so we get some */
00221    if (frame->offset < hdrlen) {
00222       f = ast_frdup(frame);
00223    }
00224 
00225    /* Construct an RTP header for our packet */
00226    rtpheader = (unsigned char *)(f->data.ptr - hdrlen);
00227    put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (multicast->seqno++) | (0 << 23)));
00228    put_unaligned_uint32(rtpheader + 4, htonl(f->ts * 8));
00229    put_unaligned_uint32(rtpheader + 8, htonl(multicast->ssrc));
00230 
00231    /* Finally send it out to the eager phones listening for us */
00232    ast_rtp_instance_get_remote_address(instance, &remote_address);
00233    res = ast_sendto(multicast->socket, (void *) rtpheader, f->datalen + hdrlen, 0, &remote_address);
00234 
00235    if (res < 0) {
00236       ast_log(LOG_ERROR, "Multicast RTP Transmission error to %s: %s\n",
00237          ast_sockaddr_stringify(&remote_address),
00238          strerror(errno));
00239    }
00240 
00241    /* If we were forced to duplicate the frame free the new one */
00242    if (frame != f) {
00243       ast_frfree(f);
00244    }
00245 
00246    return res;
00247 }
00248 
00249 /*! \brief Function called to read from a multicast instance */
00250 static struct ast_frame *multicast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
00251 {
00252    return &ast_null_frame;
00253 }
00254 
00255 static int load_module(void)
00256 {
00257    if (ast_rtp_engine_register(&multicast_rtp_engine)) {
00258       return AST_MODULE_LOAD_DECLINE;
00259    }
00260 
00261    return AST_MODULE_LOAD_SUCCESS;
00262 }
00263 
00264 static int unload_module(void)
00265 {
00266    ast_rtp_engine_unregister(&multicast_rtp_engine);
00267 
00268    return 0;
00269 }
00270 
00271 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine",
00272    .load = load_module,
00273    .unload = unload_module,
00274    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00275 );

Generated on Wed Apr 6 11:29:47 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7