Mon Oct 8 12:39:05 2012

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

Generated on Mon Oct 8 12:39:05 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7