diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h index 2f5756cd14f2043936a5d167c4a6bec832f66364..79798acd085b89a1966dad4659b2c21f92464c44 100644 --- a/include/asterisk/codec.h +++ b/include/asterisk/codec.h @@ -76,6 +76,8 @@ struct ast_codec { int (*get_length)(unsigned int samples); /*! \brief Whether the media can be smoothed or not */ unsigned int smooth; + /*! \brief Flags to be passed to the smoother */ + unsigned int smoother_flags; /*! \brief The module that registered this codec */ struct ast_module *mod; }; diff --git a/include/asterisk/format.h b/include/asterisk/format.h index b01592d16e9a3197a7cf62441f06f37247c21d84..0bad96dccc1b9b5fe70ac13b9ef32bed3b35ed2d 100644 --- a/include/asterisk/format.h +++ b/include/asterisk/format.h @@ -355,6 +355,17 @@ const char *ast_format_get_codec_name(const struct ast_format *format); */ int ast_format_can_be_smoothed(const struct ast_format *format); +/*! + * \since 13.17.0 + * + * \brief Get smoother flags for this format + * + * \param format The media format + * + * \return smoother flags for the provided format + */ +int ast_format_get_smoother_flags(const struct ast_format *format); + /*! * \brief Get the media type of a format * diff --git a/include/asterisk/smoother.h b/include/asterisk/smoother.h index e63aa77bd684ca8c6324373a337d25f7a672ec83..65ac88921fa86998db662d0443c580f318b53c18 100644 --- a/include/asterisk/smoother.h +++ b/include/asterisk/smoother.h @@ -33,6 +33,7 @@ extern "C" { #define AST_SMOOTHER_FLAG_G729 (1 << 0) #define AST_SMOOTHER_FLAG_BE (1 << 1) +#define AST_SMOOTHER_FLAG_FORCED (1 << 2) /*! \name AST_Smoother */ diff --git a/main/codec_builtin.c b/main/codec_builtin.c index 3320900c2e34f9a1e897284cb09458c20ddf0735..32ec12d3d428fcd69ddff73ea7533c58b166f8f5 100644 --- a/main/codec_builtin.c +++ b/main/codec_builtin.c @@ -36,6 +36,7 @@ #include "asterisk/format.h" #include "asterisk/format_cache.h" #include "asterisk/frame.h" +#include "asterisk/smoother.h" int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod); @@ -288,6 +289,7 @@ static struct ast_codec slin8 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin12 = { @@ -302,6 +304,7 @@ static struct ast_codec slin12 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin16 = { @@ -316,6 +319,7 @@ static struct ast_codec slin16 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin24 = { @@ -330,6 +334,7 @@ static struct ast_codec slin24 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin32 = { @@ -344,6 +349,7 @@ static struct ast_codec slin32 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin44 = { @@ -358,6 +364,7 @@ static struct ast_codec slin44 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin48 = { @@ -372,6 +379,7 @@ static struct ast_codec slin48 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin96 = { @@ -386,6 +394,7 @@ static struct ast_codec slin96 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin192 = { @@ -400,6 +409,7 @@ static struct ast_codec slin192 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static int lpc10_samples(struct ast_frame *frame) diff --git a/main/format.c b/main/format.c index 5ae5ad98623bd60bb4e56d4599850d5961d01509..09e736cf5f49017583b8a757517efe0a76328941 100644 --- a/main/format.c +++ b/main/format.c @@ -391,6 +391,11 @@ int ast_format_can_be_smoothed(const struct ast_format *format) return format->codec->smooth; } +int ast_format_get_smoother_flags(const struct ast_format *format) +{ + return format->codec->smoother_flags; +} + enum ast_media_type ast_format_get_type(const struct ast_format *format) { return format->codec->type; diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 18987cee9eb95daffab57eb4fbea0ac197fb7076..c120fc1452eb98e1be3cbba8ba3d76ecc791bfe1 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -3747,7 +3747,7 @@ static int ast_rtcp_write(const void *data) } /*! \pre instance is locked */ -static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) +static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); int pred, mark = 0; @@ -4016,10 +4016,10 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr /* If no smoother is present see if we have to set one up */ if (!rtp->smoother && ast_format_can_be_smoothed(format)) { + unsigned int smoother_flags = ast_format_get_smoother_flags(format); unsigned int framing_ms = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(instance)); - int is_slinear = ast_format_cache_is_slinear(format); - if (!framing_ms && is_slinear) { + if (!framing_ms && (smoother_flags & AST_SMOOTHER_FLAG_FORCED)) { framing_ms = ast_format_get_default_ms(format); } @@ -4030,9 +4030,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr ast_format_get_name(format), framing_ms, ast_format_get_minimum_bytes(format)); return -1; } - if (is_slinear) { - ast_smoother_set_flags(rtp->smoother, AST_SMOOTHER_FLAG_BE); - } + ast_smoother_set_flags(rtp->smoother, smoother_flags); } } @@ -4047,7 +4045,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr } while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) { - ast_rtp_raw_write(instance, f, codec); + rtp_raw_write(instance, f, codec); } } else { int hdrlen = 12; @@ -4059,7 +4057,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr f = frame; } if (f->data.ptr) { - ast_rtp_raw_write(instance, f, codec); + rtp_raw_write(instance, f, codec); } if (f != frame) { ast_frfree(f); diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c index 42de11f65e8239fbcd3e0e065c4ff6755a981552..14176da413ad697fcec73873e1d312196e555303 100644 --- a/res/res_rtp_multicast.c +++ b/res/res_rtp_multicast.c @@ -54,6 +54,7 @@ #include "asterisk/format_cache.h" #include "asterisk/multicast_rtp.h" #include "asterisk/app.h" +#include "asterisk/smoother.h" /*! Command value used for Linksys paging to indicate we are starting */ #define LINKSYS_MCAST_STARTCMD 6 @@ -95,6 +96,7 @@ struct multicast_rtp { uint16_t seqno; unsigned int lastts; struct timeval txcore; + struct ast_smoother *smoother; }; enum { @@ -395,6 +397,10 @@ static int multicast_rtp_destroy(struct ast_rtp_instance *instance) multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STOPCMD); } + if (multicast->smoother) { + ast_smoother_free(multicast->smoother); + } + close(multicast->socket); ast_free(multicast); @@ -402,43 +408,24 @@ static int multicast_rtp_destroy(struct ast_rtp_instance *instance) 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) +static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) { struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance); - struct ast_frame *f = frame; - struct ast_sockaddr remote_address; - int hdrlen = 12, res = 0, codec; - unsigned char *rtpheader; unsigned int ms = calc_txstamp(multicast, &frame->delivery); + unsigned char *rtpheader; + struct ast_sockaddr remote_address = { {0,} }; int rate = rtp_get_rate(frame->subclass.format) / 1000; + int hdrlen = 12; - /* 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 */ - codec = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance), - 1, frame->subclass.format, 0); - if (codec < 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); - } - - /* Calucate last TS */ + /* Calculate last TS */ multicast->lastts = multicast->lastts + ms * rate; - + /* Construct an RTP header for our packet */ - rtpheader = (unsigned char *)(f->data.ptr - hdrlen); + rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (multicast->seqno))); - - if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO)) { - put_unaligned_uint32(rtpheader + 4, htonl(f->ts * 8)); + + if (ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO)) { + put_unaligned_uint32(rtpheader + 4, htonl(frame->ts * 8)); } else { put_unaligned_uint32(rtpheader + 4, htonl(multicast->lastts)); } @@ -451,19 +438,86 @@ static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_fra /* Finally send it out to the eager phones listening for us */ ast_rtp_instance_get_remote_address(instance, &remote_address); - if (ast_sendto(multicast->socket, (void *) rtpheader, f->datalen + hdrlen, 0, &remote_address) < 0) { + if (ast_sendto(multicast->socket, (void *) rtpheader, frame->datalen + hdrlen, 0, &remote_address) < 0) { ast_log(LOG_ERROR, "Multicast RTP Transmission error to %s: %s\n", ast_sockaddr_stringify(&remote_address), strerror(errno)); - res = -1; + return -1; } - /* If we were forced to duplicate the frame free the new one */ - if (frame != f) { - ast_frfree(f); + 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_format *format; + struct ast_frame *f; + int codec; + + /* We only accept audio, nothing else */ + if (frame->frametype != AST_FRAME_VOICE) { + return 0; } - return res; + /* Grab the actual payload number for when we create the RTP packet */ + codec = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance), + 1, frame->subclass.format, 0); + if (codec < 0) { + return -1; + } + + format = frame->subclass.format; + if (!multicast->smoother && ast_format_can_be_smoothed(format)) { + unsigned int smoother_flags = ast_format_get_smoother_flags(format); + unsigned int framing_ms = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(instance)); + + if (!framing_ms && (smoother_flags & AST_SMOOTHER_FLAG_FORCED)) { + framing_ms = ast_format_get_default_ms(format); + } + + if (framing_ms) { + multicast->smoother = ast_smoother_new((framing_ms * ast_format_get_minimum_bytes(format)) / ast_format_get_minimum_ms(format)); + if (!multicast->smoother) { + ast_log(LOG_WARNING, "Unable to create smoother: format %s ms: %u len %u\n", + ast_format_get_name(format), framing_ms, ast_format_get_minimum_bytes(format)); + return -1; + } + ast_smoother_set_flags(multicast->smoother, smoother_flags); + } + } + + if (multicast->smoother) { + if (ast_smoother_test_flag(multicast->smoother, AST_SMOOTHER_FLAG_BE)) { + ast_smoother_feed_be(multicast->smoother, frame); + } else { + ast_smoother_feed(multicast->smoother, frame); + } + + while ((f = ast_smoother_read(multicast->smoother)) && f->data.ptr) { + rtp_raw_write(instance, f, codec); + } + } else { + int hdrlen = 12; + + /* 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); + } else { + f = frame; + } + + if (f->data.ptr) { + rtp_raw_write(instance, f, codec); + } + + if (f != frame) { + ast_frfree(f); + } + } + + return 0; } /*! \brief Function called to read from a multicast instance */