Thu Sep 7 01:03:04 2017

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: 400393 $")
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    unsigned int lastts;
00094    struct timeval txcore;
00095 };
00096 
00097 /* Forward Declarations */
00098 static int multicast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct ast_sockaddr *addr, void *data);
00099 static int multicast_rtp_activate(struct ast_rtp_instance *instance);
00100 static int multicast_rtp_destroy(struct ast_rtp_instance *instance);
00101 static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame);
00102 static struct ast_frame *multicast_rtp_read(struct ast_rtp_instance *instance, int rtcp);
00103 
00104 /* RTP Engine Declaration */
00105 static struct ast_rtp_engine multicast_rtp_engine = {
00106    .name = "multicast",
00107    .new = multicast_rtp_new,
00108    .activate = multicast_rtp_activate,
00109    .destroy = multicast_rtp_destroy,
00110    .write = multicast_rtp_write,
00111    .read = multicast_rtp_read,
00112 };
00113 
00114 /*! \brief Function called to create a new multicast instance */
00115 static int multicast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct ast_sockaddr *addr, void *data)
00116 {
00117    struct multicast_rtp *multicast;
00118    const char *type = data;
00119 
00120    if (!(multicast = ast_calloc(1, sizeof(*multicast)))) {
00121       return -1;
00122    }
00123 
00124    if (!strcasecmp(type, "basic")) {
00125       multicast->type = MULTICAST_TYPE_BASIC;
00126    } else if (!strcasecmp(type, "linksys")) {
00127       multicast->type = MULTICAST_TYPE_LINKSYS;
00128    } else {
00129       ast_free(multicast);
00130       return -1;
00131    }
00132 
00133    if ((multicast->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
00134       ast_free(multicast);
00135       return -1;
00136    }
00137 
00138    multicast->ssrc = ast_random();
00139 
00140    ast_rtp_instance_set_data(instance, multicast);
00141 
00142    return 0;
00143 }
00144 
00145 static int rtp_get_rate(format_t format)
00146 {
00147         return (format == AST_FORMAT_G722) ? 8000 : ast_format_rate(format);
00148 }
00149 
00150 static unsigned int calc_txstamp(struct multicast_rtp *rtp, struct timeval *delivery)
00151 {
00152         struct timeval t;
00153         long ms;
00154 
00155         if (ast_tvzero(rtp->txcore)) {
00156                 rtp->txcore = ast_tvnow();
00157                 rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000;
00158         }
00159 
00160         t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow();
00161         if ((ms = ast_tvdiff_ms(t, rtp->txcore)) < 0) {
00162                 ms = 0;
00163         }
00164         rtp->txcore = t;
00165 
00166         return (unsigned int) ms;
00167 }
00168 
00169 /*! \brief Helper function which populates a control packet with useful information and sends it */
00170 static int multicast_send_control_packet(struct ast_rtp_instance *instance, struct multicast_rtp *multicast, int command)
00171 {
00172    struct multicast_control_packet control_packet = { .unique_id = htonl((u_long)time(NULL)),
00173                         .command = htonl(command),
00174    };
00175    struct ast_sockaddr control_address, remote_address;
00176 
00177    ast_rtp_instance_get_local_address(instance, &control_address);
00178    ast_rtp_instance_get_remote_address(instance, &remote_address);
00179 
00180    /* Ensure the user of us have given us both the control address and destination address */
00181    if (ast_sockaddr_isnull(&control_address) ||
00182        ast_sockaddr_isnull(&remote_address)) {
00183       return -1;
00184    }
00185 
00186    /* The protocol only supports IPv4. */
00187    if (ast_sockaddr_is_ipv6(&remote_address)) {
00188       ast_log(LOG_WARNING, "Cannot send control packet for IPv6 "
00189          "remote address.\n");
00190       return -1;
00191    }
00192 
00193    control_packet.ip = htonl(ast_sockaddr_ipv4(&remote_address));
00194    control_packet.port = htonl(ast_sockaddr_port(&remote_address));
00195 
00196    /* Based on a recommendation by Brian West who did the FreeSWITCH implementation we send control packets twice */
00197    ast_sendto(multicast->socket, &control_packet, sizeof(control_packet), 0, &control_address);
00198    ast_sendto(multicast->socket, &control_packet, sizeof(control_packet), 0, &control_address);
00199 
00200    return 0;
00201 }
00202 
00203 /*! \brief Function called to indicate that audio is now going to flow */
00204 static int multicast_rtp_activate(struct ast_rtp_instance *instance)
00205 {
00206    struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
00207 
00208    if (multicast->type != MULTICAST_TYPE_LINKSYS) {
00209       return 0;
00210    }
00211 
00212    return multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STARTCMD);
00213 }
00214 
00215 /*! \brief Function called to destroy a multicast instance */
00216 static int multicast_rtp_destroy(struct ast_rtp_instance *instance)
00217 {
00218    struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
00219 
00220    if (multicast->type == MULTICAST_TYPE_LINKSYS) {
00221       multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STOPCMD);
00222    }
00223 
00224    close(multicast->socket);
00225 
00226    ast_free(multicast);
00227 
00228    return 0;
00229 }
00230 
00231 /*! \brief Function called to broadcast some audio on a multicast instance */
00232 static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
00233 {
00234    struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance);
00235    struct ast_frame *f = frame;
00236    struct ast_sockaddr remote_address;
00237    int hdrlen = 12, res = 0, codec;
00238    int rate;
00239    unsigned char *rtpheader;
00240    unsigned int ms = calc_txstamp(multicast, &frame->delivery);
00241 
00242    /* We only accept audio, nothing else */
00243    if (frame->frametype != AST_FRAME_VOICE) {
00244       return 0;
00245    }
00246    rate = rtp_get_rate(frame->subclass.codec) / 1000;
00247 
00248    /* Grab the actual payload number for when we create the RTP packet */
00249    if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, frame->subclass.codec)) < 0) {
00250       return -1;
00251    }
00252 
00253    /* If we do not have space to construct an RTP header duplicate the frame so we get some */
00254    if (frame->offset < hdrlen) {
00255       f = ast_frdup(frame);
00256    }
00257 
00258    /* Calucate last TS */
00259    multicast->lastts = multicast->lastts + ms * rate;
00260 
00261    /* Construct an RTP header for our packet */
00262    rtpheader = (unsigned char *)(f->data.ptr - hdrlen);
00263    put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (multicast->seqno)));
00264 
00265    if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO)) {
00266       put_unaligned_uint32(rtpheader + 4, htonl(f->ts * 8));
00267    } else {
00268       put_unaligned_uint32(rtpheader + 4, htonl(multicast->lastts));
00269    }
00270 
00271    put_unaligned_uint32(rtpheader + 8, htonl(multicast->ssrc));
00272 
00273    /* Increment sequence number and wrap to 0 if it overflows 16 bits. */
00274    multicast->seqno = 0xFFFF & (multicast->seqno + 1);
00275 
00276    /* Finally send it out to the eager phones listening for us */
00277    ast_rtp_instance_get_remote_address(instance, &remote_address);
00278 
00279    if (ast_sendto(multicast->socket, (void *) rtpheader, f->datalen + hdrlen, 0, &remote_address) < 0) {
00280       ast_log(LOG_ERROR, "Multicast RTP Transmission error to %s: %s\n",
00281          ast_sockaddr_stringify(&remote_address),
00282          strerror(errno));
00283       res = -1;
00284    }
00285 
00286    /* If we were forced to duplicate the frame free the new one */
00287    if (frame != f) {
00288       ast_frfree(f);
00289    }
00290 
00291    return res;
00292 }
00293 
00294 /*! \brief Function called to read from a multicast instance */
00295 static struct ast_frame *multicast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
00296 {
00297    return &ast_null_frame;
00298 }
00299 
00300 static int load_module(void)
00301 {
00302    if (ast_rtp_engine_register(&multicast_rtp_engine)) {
00303       return AST_MODULE_LOAD_DECLINE;
00304    }
00305 
00306    return AST_MODULE_LOAD_SUCCESS;
00307 }
00308 
00309 static int unload_module(void)
00310 {
00311    ast_rtp_engine_unregister(&multicast_rtp_engine);
00312 
00313    return 0;
00314 }
00315 
00316 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine",
00317    .load = load_module,
00318    .unload = unload_module,
00319    .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00320 );

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1