diff options
-rw-r--r-- | CHANGES | 30 | ||||
-rw-r--r-- | channels/chan_multicast_rtp.c | 7 | ||||
-rw-r--r-- | channels/chan_rtp.c | 316 | ||||
-rw-r--r-- | include/asterisk/multicast_rtp.h | 58 | ||||
-rw-r--r-- | res/res_rtp_multicast.c | 189 | ||||
-rw-r--r-- | res/res_rtp_multicast.exports.in | 6 |
6 files changed, 533 insertions, 73 deletions
@@ -17,6 +17,36 @@ Core * A channel variable FORWARDERNAME is now set which indicates which channel was responsible for a forwarding requests received on dial attempt. +chan_multicast_rtp +------------------ + * Deprecated in favor of chan_rtp which is basically chan_multicast_rtp + renamed to chan_rtp with UnicastRTP channels added and some internal code + improvements. + +chan_rtp +------------------ + * The format for dialing a unicast RTP channel is: + UnicastRTP/<destination-addr>[/[<options>]] + Where <destination-addr> is something like '127.0.0.1:5060'. + Where <options> are in standard Asterisk flag options format: + c(<codec>) - Specify which codec/format to use such as 'ulaw'. + e(<engine>) - Specify which RTP engine to use such as 'asterisk'. + + * More options are available over what chan_multicast_rtp supports. + The format for dialing a multicast RTP channel is: + MulticastRTP/<type>/<destination-addr>[/[<control-addr>][/[<options>]]] + Where <type> can be either 'basic' or 'linksys'. + Where <destination-addr> is something like '224.0.0.3:5060'. + Where <control-addr> is something like '127.0.0.1:5060'. + Where <options> are in standard Asterisk flag options format: + c(<codec>) - Specify which codec/format to use such as 'ulaw'. + i(<address>) - Specify the interface address from which multicast RTP + is sent. + l(<enable>) - Set whether packets are looped back to the sender. The + enable value can be 0 to set looping to off and non-zero to set + looping on. + t(<ttl>) - Set the time-to-live (TTL) value for multicast packets. + func_odbc ------------------ * Added new global option "single_db_connection". diff --git a/channels/chan_multicast_rtp.c b/channels/chan_multicast_rtp.c index 267baabf1..c45dedf7f 100644 --- a/channels/chan_multicast_rtp.c +++ b/channels/chan_multicast_rtp.c @@ -28,7 +28,8 @@ */ /*** MODULEINFO - <support_level>core</support_level> + <support_level>deprecated</support_level> + <defaultenabled>no</defaultenabled> ***/ #include "asterisk.h" @@ -215,8 +216,8 @@ static int unload_module(void) return 0; } -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Paging Channel", - .support_level = AST_MODULE_SUPPORT_CORE, +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Paging Channel (use chan_rtp instead)", + .support_level = AST_MODULE_SUPPORT_DEPRECATED, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, diff --git a/channels/chan_rtp.c b/channels/chan_rtp.c index 267baabf1..0fe66bd20 100644 --- a/channels/chan_rtp.c +++ b/channels/chan_rtp.c @@ -1,7 +1,7 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2009, Digium, Inc. + * Copyright (C) 2009 - 2014, Digium, Inc. * * Joshua Colp <jcolp@digium.com> * Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.com> @@ -22,7 +22,7 @@ * \author Joshua Colp <jcolp@digium.com> * \author Andreas 'MacBrody' Broadmann <andreas.brodmann@gmail.com> * - * \brief Multicast RTP Paging Channel + * \brief RTP (Multicast and Unicast) Media Channel * * \ingroup channel_drivers */ @@ -33,54 +33,64 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +ASTERISK_REGISTER_FILE() -#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"; +#include "asterisk/format_cache.h" +#include "asterisk/multicast_rtp.h" /* Forward declarations */ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause); -static int multicast_rtp_call(struct ast_channel *ast, const 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); +static struct ast_channel *unicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause); +static int rtp_call(struct ast_channel *ast, const char *dest, int timeout); +static int rtp_hangup(struct ast_channel *ast); +static struct ast_frame *rtp_read(struct ast_channel *ast); +static int rtp_write(struct ast_channel *ast, struct ast_frame *f); -/* Channel driver declaration */ +/* Multicast channel driver declaration */ static struct ast_channel_tech multicast_rtp_tech = { .type = "MulticastRTP", - .description = tdesc, + .description = "Multicast RTP Paging Channel Driver", .requester = multicast_rtp_request, - .call = multicast_rtp_call, - .hangup = multicast_rtp_hangup, - .read = multicast_rtp_read, - .write = multicast_rtp_write, + .call = rtp_call, + .hangup = rtp_hangup, + .read = rtp_read, + .write = rtp_write, +}; + +/* Unicast channel driver declaration */ +static struct ast_channel_tech unicast_rtp_tech = { + .type = "UnicastRTP", + .description = "Unicast RTP Media Channel Driver", + .requester = unicast_rtp_request, + .call = rtp_call, + .hangup = rtp_hangup, + .read = rtp_read, + .write = 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) +static struct ast_frame *rtp_read(struct ast_channel *ast) { - return &ast_null_frame; + struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast); + int fdno = ast_channel_fdno(ast); + + switch (fdno) { + case 0: + return ast_rtp_instance_read(instance, 0); + default: + 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) +static int rtp_write(struct ast_channel *ast, struct ast_frame *f) { struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast); @@ -88,7 +98,7 @@ static int multicast_rtp_write(struct ast_channel *ast, struct ast_frame *f) } /*! \brief Function called when we should actually call the destination */ -static int multicast_rtp_call(struct ast_channel *ast, const char *dest, int timeout) +static int rtp_call(struct ast_channel *ast, const char *dest, int timeout) { struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast); @@ -98,7 +108,7 @@ static int multicast_rtp_call(struct ast_channel *ast, const char *dest, int tim } /*! \brief Function called when we should hang the channel up */ -static int multicast_rtp_hangup(struct ast_channel *ast) +static int rtp_hangup(struct ast_channel *ast) { struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast); @@ -109,41 +119,65 @@ static int multicast_rtp_hangup(struct ast_channel *ast) return 0; } -/*! \brief Function called when we should prepare to call the destination */ +/*! \brief Function called when we should prepare to call the multicast destination */ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause) { - char *tmp = ast_strdupa(data), *multicast_type = tmp, *destination, *control; + char *parse; struct ast_rtp_instance *instance; struct ast_sockaddr control_address; struct ast_sockaddr destination_address; struct ast_channel *chan; struct ast_format_cap *caps = NULL; struct ast_format *fmt = NULL; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(type); + AST_APP_ARG(destination); + AST_APP_ARG(control); + AST_APP_ARG(options); + ); + struct ast_multicast_rtp_options *mcast_options = NULL; + + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "A multicast type and destination must be given to the 'MulticastRTP' channel\n"); + goto failure; + } + parse = ast_strdupa(data); + AST_NONSTANDARD_APP_ARGS(args, parse, '/'); - fmt = ast_format_cap_get_format(cap, 0); - - ast_sockaddr_setnull(&control_address); + if (ast_strlen_zero(args.type)) { + ast_log(LOG_ERROR, "Type is required for the 'MulticastRTP' channel\n"); + goto failure; + } - /* If no type was given we can't do anything */ - if (ast_strlen_zero(multicast_type)) { + if (ast_strlen_zero(args.destination)) { + ast_log(LOG_ERROR, "Destination is required for the 'MulticastRTP' channel\n"); + goto failure; + } + if (!ast_sockaddr_parse(&destination_address, args.destination, PARSE_PORT_REQUIRE)) { + ast_log(LOG_ERROR, "Destination address '%s' could not be parsed\n", + args.destination); goto failure; } - if (!(destination = strchr(tmp, '/'))) { + ast_sockaddr_setnull(&control_address); + if (!ast_strlen_zero(args.control) + && !ast_sockaddr_parse(&control_address, args.control, PARSE_PORT_REQUIRE)) { + ast_log(LOG_ERROR, "Control address '%s' could not be parsed\n", args.control); goto failure; } - *destination++ = '\0'; - if ((control = strchr(destination, '/'))) { - *control++ = '\0'; - if (!ast_sockaddr_parse(&control_address, control, - PARSE_PORT_REQUIRE)) { - goto failure; - } + mcast_options = ast_multicast_rtp_create_options(args.type, args.options); + if (!mcast_options) { + goto failure; } - if (!ast_sockaddr_parse(&destination_address, destination, - PARSE_PORT_REQUIRE)) { + fmt = ast_multicast_rtp_options_get_format(mcast_options); + if (!fmt) { + fmt = ast_format_cap_get_format(cap, 0); + } + if (!fmt) { + ast_log(LOG_ERROR, "No codec available for sending RTP to '%s'\n", + args.destination); goto failure; } @@ -152,11 +186,17 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo goto failure; } - if (!(instance = ast_rtp_instance_new("multicast", NULL, &control_address, multicast_type))) { + instance = ast_rtp_instance_new("multicast", NULL, &control_address, mcast_options); + if (!instance) { + ast_log(LOG_ERROR, + "Could not create '%s' multicast RTP instance for sending media to '%s'\n", + args.type, args.destination); goto failure; } - if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids, requestor, 0, "MulticastRTP/%p", instance))) { + chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids, + requestor, 0, "MulticastRTP/%p", instance); + if (!chan) { ast_rtp_instance_destroy(instance); goto failure; } @@ -178,44 +218,196 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo ao2_ref(fmt, -1); ao2_ref(caps, -1); + ast_multicast_rtp_free_options(mcast_options); return chan; failure: ao2_cleanup(fmt); ao2_cleanup(caps); + ast_multicast_rtp_free_options(mcast_options); *cause = AST_CAUSE_FAILURE; return NULL; } -/*! \brief Function called when our module is loaded */ -static int load_module(void) +enum { + OPT_RTP_CODEC = (1 << 0), + OPT_RTP_ENGINE = (1 << 1), +}; + +enum { + OPT_ARG_RTP_CODEC, + OPT_ARG_RTP_ENGINE, + /* note: this entry _MUST_ be the last one in the enum */ + OPT_ARG_ARRAY_SIZE +}; + +AST_APP_OPTIONS(unicast_rtp_options, BEGIN_OPTIONS + /*! Set the codec to be used for unicast RTP */ + AST_APP_OPTION_ARG('c', OPT_RTP_CODEC, OPT_ARG_RTP_CODEC), + /*! Set the RTP engine to use for unicast RTP */ + AST_APP_OPTION_ARG('e', OPT_RTP_ENGINE, OPT_ARG_RTP_ENGINE), +END_OPTIONS ); + +/*! \brief Function called when we should prepare to call the unicast destination */ +static struct ast_channel *unicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause) { - if (!(multicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { - return AST_MODULE_LOAD_DECLINE; + char *parse; + struct ast_rtp_instance *instance; + struct ast_sockaddr address; + struct ast_sockaddr local_address; + struct ast_channel *chan; + struct ast_format_cap *caps = NULL; + struct ast_format *fmt = NULL; + const char *engine_name; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(destination); + AST_APP_ARG(options); + ); + struct ast_flags opts = { 0, }; + char *opt_args[OPT_ARG_ARRAY_SIZE]; + + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "Destination is required for the 'UnicastRTP' channel\n"); + goto failure; } - ast_format_cap_append_by_type(multicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN); - if (ast_channel_register(&multicast_rtp_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'MulticastRTP'\n"); - ao2_ref(multicast_rtp_tech.capabilities, -1); - multicast_rtp_tech.capabilities = NULL; - return AST_MODULE_LOAD_DECLINE; + parse = ast_strdupa(data); + AST_NONSTANDARD_APP_ARGS(args, parse, '/'); + + if (ast_strlen_zero(args.destination)) { + ast_log(LOG_ERROR, "Destination is required for the 'UnicastRTP' channel\n"); + goto failure; + } + if (!ast_sockaddr_parse(&address, args.destination, PARSE_PORT_REQUIRE)) { + ast_log(LOG_ERROR, "Destination '%s' could not be parsed\n", args.destination); + goto failure; } - return AST_MODULE_LOAD_SUCCESS; + if (!ast_strlen_zero(args.options) + && ast_app_parse_options(unicast_rtp_options, &opts, opt_args, + ast_strdupa(args.options))) { + ast_log(LOG_ERROR, "'UnicastRTP' channel options '%s' parse error\n", + args.options); + goto failure; + } + + if (ast_test_flag(&opts, OPT_RTP_CODEC) + && !ast_strlen_zero(opt_args[OPT_ARG_RTP_CODEC])) { + fmt = ast_format_cache_get(opt_args[OPT_ARG_RTP_CODEC]); + if (!fmt) { + ast_log(LOG_ERROR, "Codec '%s' not found for sending RTP to '%s'\n", + opt_args[OPT_ARG_RTP_CODEC], args.destination); + goto failure; + } + } else { + fmt = ast_format_cap_get_format(cap, 0); + if (!fmt) { + ast_log(LOG_ERROR, "No codec available for sending RTP to '%s'\n", + args.destination); + goto failure; + } + } + + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!caps) { + goto failure; + } + + engine_name = S_COR(ast_test_flag(&opts, OPT_RTP_ENGINE), + opt_args[OPT_ARG_RTP_ENGINE], NULL); + + ast_ouraddrfor(&address, &local_address); + instance = ast_rtp_instance_new(engine_name, NULL, &local_address, NULL); + if (!instance) { + ast_log(LOG_ERROR, + "Could not create %s RTP instance for sending media to '%s'\n", + S_OR(engine_name, "default"), args.destination); + goto failure; + } + + chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids, + requestor, 0, "UnicastRTP/%s-%p", args.destination, instance); + if (!chan) { + ast_rtp_instance_destroy(instance); + goto failure; + } + ast_rtp_instance_set_channel_id(instance, ast_channel_uniqueid(chan)); + ast_rtp_instance_set_remote_address(instance, &address); + ast_channel_set_fd(chan, 0, ast_rtp_instance_fd(instance, 0)); + + ast_channel_tech_set(chan, &unicast_rtp_tech); + + ast_format_cap_append(caps, fmt, 0); + ast_channel_nativeformats_set(chan, caps); + ast_channel_set_writeformat(chan, fmt); + ast_channel_set_rawwriteformat(chan, fmt); + ast_channel_set_readformat(chan, fmt); + ast_channel_set_rawreadformat(chan, fmt); + + ast_channel_tech_pvt_set(chan, instance); + + pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_ADDRESS", + ast_sockaddr_stringify_addr(&local_address)); + ast_rtp_instance_get_local_address(instance, &local_address); + pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_PORT", + ast_sockaddr_stringify_port(&local_address)); + + ast_channel_unlock(chan); + + ao2_ref(fmt, -1); + ao2_ref(caps, -1); + + return chan; + +failure: + ao2_cleanup(fmt); + ao2_cleanup(caps); + *cause = AST_CAUSE_FAILURE; + return NULL; } /*! \brief Function called when our module is unloaded */ static int unload_module(void) { ast_channel_unregister(&multicast_rtp_tech); - ao2_ref(multicast_rtp_tech.capabilities, -1); + ao2_cleanup(multicast_rtp_tech.capabilities); multicast_rtp_tech.capabilities = NULL; + ast_channel_unregister(&unicast_rtp_tech); + ao2_cleanup(unicast_rtp_tech.capabilities); + unicast_rtp_tech.capabilities = NULL; + return 0; } -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Paging Channel", +/*! \brief Function called when our module is loaded */ +static int load_module(void) +{ + if (!(multicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + return AST_MODULE_LOAD_DECLINE; + } + ast_format_cap_append_by_type(multicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN); + if (ast_channel_register(&multicast_rtp_tech)) { + ast_log(LOG_ERROR, "Unable to register channel class 'MulticastRTP'\n"); + unload_module(); + return AST_MODULE_LOAD_DECLINE; + } + + if (!(unicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + unload_module(); + return AST_MODULE_LOAD_DECLINE; + } + ast_format_cap_append_by_type(unicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN); + if (ast_channel_register(&unicast_rtp_tech)) { + ast_log(LOG_ERROR, "Unable to register channel class 'UnicastRTP'\n"); + unload_module(); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "RTP Media Channel", .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, diff --git a/include/asterisk/multicast_rtp.h b/include/asterisk/multicast_rtp.h new file mode 100644 index 000000000..c286c1f96 --- /dev/null +++ b/include/asterisk/multicast_rtp.h @@ -0,0 +1,58 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.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. + */ + +#ifndef MULTICAST_RTP_H_ +#define MULTICAST_RTP_H_ +struct ast_multicast_rtp_options; + +/*! + * \brief Create multicast RTP options. + * + * These are passed to the multicast RTP engine on its creation. + * + * \param type The type of multicast RTP, either "basic" or "linksys" + * \param options Miscellaneous options + * \retval NULL Failure + * \retval non-NULL success + */ +struct ast_multicast_rtp_options *ast_multicast_rtp_create_options(const char *type, + const char *options); + +/*! + * \brief Free multicast RTP options + * + * This function is NULL-tolerant + * + * \param mcast_options Options to free + */ +void ast_multicast_rtp_free_options(struct ast_multicast_rtp_options *mcast_options); + +/*! + * \brief Get format specified in multicast options + * + * Multicast options allow for a format to be selected. + * This function accesses the selected format and creates + * an ast_format structure for it. + * + * \param mcast_options The options where a codec was specified + * \retval NULL No format specified in the options + * \revval non-NULL The format to use for communication + */ +struct ast_format *ast_multicast_rtp_options_get_format(struct ast_multicast_rtp_options *mcast_options); + +#endif /* MULTICAST_RTP_H_ */ diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c index 8327cf20a..1b923474c 100644 --- a/res/res_rtp_multicast.c +++ b/res/res_rtp_multicast.c @@ -54,6 +54,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/rtp_engine.h" #include "asterisk/format_cache.h" +#include "asterisk/multicast_rtp.h" +#include "asterisk/app.h" /*! Command value used for Linksys paging to indicate we are starting */ #define LINKSYS_MCAST_STARTCMD 6 @@ -63,8 +65,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /*! \brief Type of paging to do */ enum multicast_type { + /*! Type has not been set yet */ + MULTICAST_TYPE_UNSPECIFIED = 0, /*! Simple multicast enabled client/receiver paging like Snom and Barix uses */ - MULTICAST_TYPE_BASIC = 0, + MULTICAST_TYPE_BASIC, /*! More advanced Linksys type paging which requires a start and stop packet */ MULTICAST_TYPE_LINKSYS, }; @@ -95,6 +99,91 @@ struct multicast_rtp { struct timeval txcore; }; +enum { + OPT_CODEC = (1 << 0), + OPT_LOOP = (1 << 1), + OPT_TTL = (1 << 2), + OPT_IF = (1 << 3), +}; + +enum { + OPT_ARG_CODEC = 0, + OPT_ARG_LOOP, + OPT_ARG_TTL, + OPT_ARG_IF, + OPT_ARG_ARRAY_SIZE, +}; + +AST_APP_OPTIONS(multicast_rtp_options, BEGIN_OPTIONS + /*! Set the codec to be used for multicast RTP */ + AST_APP_OPTION_ARG('c', OPT_CODEC, OPT_ARG_CODEC), + /*! Set whether multicast RTP is looped back to the sender */ + AST_APP_OPTION_ARG('l', OPT_LOOP, OPT_ARG_LOOP), + /*! Set the hop count for multicast RTP */ + AST_APP_OPTION_ARG('t', OPT_TTL, OPT_ARG_TTL), + /*! Set the interface from which multicast RTP is sent */ + AST_APP_OPTION_ARG('i', OPT_IF, OPT_ARG_IF), +END_OPTIONS ); + +struct ast_multicast_rtp_options { + char *type; + char *options; + struct ast_format *fmt; + struct ast_flags opts; + char *opt_args[OPT_ARG_ARRAY_SIZE]; + /*! The type and options are stored in this buffer */ + char buf[0]; +}; + +struct ast_multicast_rtp_options *ast_multicast_rtp_create_options(const char *type, + const char *options) +{ + struct ast_multicast_rtp_options *mcast_options; + char *pos; + + mcast_options = ast_calloc(1, sizeof(*mcast_options) + + strlen(type) + + strlen(options) + 2); + if (!mcast_options) { + return NULL; + } + + pos = mcast_options->buf; + + /* Safe */ + strcpy(pos, type); + mcast_options->type = pos; + pos += strlen(type) + 1; + + /* Safe */ + strcpy(pos, options); + mcast_options->options = pos; + + if (ast_app_parse_options(multicast_rtp_options, &mcast_options->opts, + mcast_options->opt_args, mcast_options->options)) { + ast_log(LOG_WARNING, "Error parsing multicast RTP options\n"); + ast_multicast_rtp_free_options(mcast_options); + return NULL; + } + + return mcast_options; +} + +void ast_multicast_rtp_free_options(struct ast_multicast_rtp_options *mcast_options) +{ + ast_free(mcast_options); +} + +struct ast_format *ast_multicast_rtp_options_get_format(struct ast_multicast_rtp_options *mcast_options) +{ + if (ast_test_flag(&mcast_options->opts, OPT_CODEC) + && !ast_strlen_zero(mcast_options->opt_args[OPT_ARG_CODEC])) { + return ast_format_cache_get(mcast_options->opt_args[OPT_ARG_CODEC]); + } + + return NULL; +} + /* Forward Declarations */ static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data); static int multicast_rtp_activate(struct ast_rtp_instance *instance); @@ -112,21 +201,93 @@ static struct ast_rtp_engine multicast_rtp_engine = { .read = multicast_rtp_read, }; +static int set_type(struct multicast_rtp *multicast, const char *type) +{ + if (!strcasecmp(type, "basic")) { + multicast->type = MULTICAST_TYPE_BASIC; + } else if (!strcasecmp(type, "linksys")) { + multicast->type = MULTICAST_TYPE_LINKSYS; + } else { + ast_log(LOG_WARNING, "Unrecognized multicast type '%s' specified.\n", type); + return -1; + } + + return 0; +} + +static void set_ttl(int sock, const char *ttl_str) +{ + int ttl; + + if (ast_strlen_zero(ttl_str)) { + return; + } + + ast_debug(3, "Setting multicast TTL to %s\n", ttl_str); + + if (sscanf(ttl_str, "%30d", &ttl) < 1) { + ast_log(LOG_WARNING, "Inavlid multicast ttl option '%s'\n", ttl_str); + return; + } + + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { + ast_log(LOG_WARNING, "Could not set multicast ttl to '%s': %s\n", + ttl_str, strerror(errno)); + } +} + +static void set_loop(int sock, const char *loop_str) +{ + unsigned char loop; + + if (ast_strlen_zero(loop_str)) { + return; + } + + ast_debug(3, "Setting multicast loop to %s\n", loop_str); + + if (sscanf(loop_str, "%30hhu", &loop) < 1) { + ast_log(LOG_WARNING, "Invalid multicast loop option '%s'\n", loop_str); + return; + } + + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) { + ast_log(LOG_WARNING, "Could not set multicast loop to '%s': %s\n", + loop_str, strerror(errno)); + } +} + +static void set_if(int sock, const char *if_str) +{ + struct in_addr iface; + + if (ast_strlen_zero(if_str)) { + return; + } + + ast_debug(3, "Setting multicast if to %s\n", if_str); + + if (!inet_aton(if_str, &iface)) { + ast_log(LOG_WARNING, "Cannot parse if option '%s'\n", if_str); + } + + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iface, sizeof(iface)) < 0) { + ast_log(LOG_WARNING, "Could not set multicast if to '%s': %s\n", + if_str, strerror(errno)); + } +} + /*! \brief Function called to create a new multicast instance */ static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data) { struct multicast_rtp *multicast; - const char *type = data; + struct ast_multicast_rtp_options *mcast_options = 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 { + if (set_type(multicast, mcast_options->type)) { ast_free(multicast); return -1; } @@ -136,6 +297,18 @@ static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched return -1; } + if (ast_test_flag(&mcast_options->opts, OPT_LOOP)) { + set_loop(multicast->socket, mcast_options->opt_args[OPT_ARG_LOOP]); + } + + if (ast_test_flag(&mcast_options->opts, OPT_TTL)) { + set_ttl(multicast->socket, mcast_options->opt_args[OPT_ARG_TTL]); + } + + if (ast_test_flag(&mcast_options->opts, OPT_IF)) { + set_if(multicast->socket, mcast_options->opt_args[OPT_ARG_IF]); + } + multicast->ssrc = ast_random(); ast_rtp_instance_set_data(instance, multicast); @@ -314,7 +487,7 @@ static int unload_module(void) return 0; } -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine", +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine", .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, diff --git a/res/res_rtp_multicast.exports.in b/res/res_rtp_multicast.exports.in new file mode 100644 index 000000000..995a1802e --- /dev/null +++ b/res/res_rtp_multicast.exports.in @@ -0,0 +1,6 @@ +{ + global: + LINKER_SYMBOL_PREFIXast_multicast_rtp*; + local: + *; +}; |